Visual Prolog 11 Upgrade Notes

From wiki.visual-prolog.com

Revision as of 13:58, 11 January 2024 by Thomas Linder Puls (talk | contribs) (Release)

This document describes how to upgrade Visual Prolog 10 projects to Visual Prolog 11, for older projects you should also read the Visual Prolog 10 Upgrade Notes.

If you have problems with upgrading your projects and need additional information, you are welcome to ask questions in Visual Prolog Discussion Forum.

By default, the installation of Visual Prolog 11 will not replace any older installed versions. It is possible to work with several versions on a single computer. See also vipLaunch.

The Commercial and Personal Edition are identical; differences in capabilities are controlled by the license used to activate the computer.

Entities that are not available to Personal Edition usage are marked with the attribute [feature("Commercial Edition")].

Visual Prolog 11 is installed per user in ...\Users\<user>\AppData\Roaming\Visual Prolog\11.

Visual Prolog 11 project files are backward-compatible with Visual Prolog 10 project files.

Notice it is highly advisable to have a backup of a project before updating.

When updating a project you should open the project in the new IDE and accept to remove any non-existing PFC packages. Then build the project and accept to

  • delete all bad include directives
  • insert new ones and
  • add new PFC packages

Superfluous Files

If you haven't removed the superfluous resource files then see Visual Prolog 10 Upgrade Notes#Superfluous_files.

gdiplus

If your project initializes gdiplus, e.g. like this:

clauses
    run() :-
        _ = mainForm::display(gui::getScreenWindow()),
        GDIpToken = gdiplus::startup(),
        messageLoop::run(),
        gdiplus::shutdown(GDIpToken).

Then simply remove the calls to gdiplus::startup and gdiplus::shutdown code:

clauses
    run() :-
        _ = mainForm::display(gui::getScreenWindow()),
        messageLoop::run().

The color class/interface has been retired, and superseded by the domain gdiplus_native::color. In many cases you simply have to remove the call to color::create.

This line (in the 'GDI+ Example)

        DarkRed = pen::createColor(color::create(gdiplus_native::darkred), penWidth, unit),

has been changed to:

        DarkRed = pen::createColor(gdiplus_native::darkred, penWidth, unit),

A little editor trick: if you use autoformatting of code (a project setting) you can just remove color::create:

        DarkRed = pen::createColor((gdiplus_native::darkred), penWidth, unit),

When you save the file the superfluous parentheses will be removed automatically. Therefore you can correct an entire file simply by replacing all occurrences of color::create with nothing/blank.

Furthermore, some domains have been relocated from the gdiplus class to the gdiplus_native class. The "Fix" button in the error window should handle that problem automatically. Otherwise, the IDE command Insert -> Qualification Ctrl+Shift+S can also insert or correct the qualification.

The object predicate graphics::drawString/6 has been retired, instead you will have to choose between:

predicates
    drawStringIn : (font Font, brush Brush, rectF LayoutRect, string String, stringFormat StringFormat = stringFormat::null).
    drawStringAt : (font Font, brush Brush, pointF Origin, string String, stringFormat StringFormat = stringFormat::null).
  • graphics::drawStringIn draws the string in the rectangle LayoutRect or
  • graphics::drawStringAt draws the string at the point Origin

treeModel_std & treeNode_std

If you use the treeControl with the treeModel_std you will have to add code::value as argument to treeModel_std and treeNode_std. Notice that this is not necessary in clauses; only in other places (declarations, ...).

Here illustrated with code from the dragNdropDemo in the examples:

implement mainForm inherits formWindow
    open core, vpiDomains, treeModel_std{core::value}
...
predicates
    onDragBegin : treeControl{treeNode_std{value}}::dragBeginResponder.
...
% This code is maintained automatically, do not update it manually. 10:41:58-23.11.2011
facts
    treeCtl : treecontrol{treeNode_std{value}}.
...

Notice that if you have deleted the form/dialog file (as described in superfluous Files) then you can (despite the warning in the comment) edit directly in the "auto"-code.

codePageId ansi & threadAnsi

The following codePageID's have been deprecated:

constants
    ansi : codePage = 0 [deprecated("It is recommended not to use this codepage, but use an explicit encoding instead")].
    threadAnsi : codePage = 3 [deprecated("It is recommended not to use this codepage, but use an explicit encoding instead")].
    % @short Represents the systems/threads current ANSI codePage.
    % It is recommended not to use this codepage but use an explicit encoding instead.
    % @end

The problem with these system/user codepages is that they are often set to some regional codepage with 8 bit characters. Files written like must be read with the same codepage, otherwise characters may become wrong or even lost.

Some time ago (unknown when) Microsoft have introduced the possibility to use utf-8 as system/user codepage. Using utf-8 is technically the best choice, because it "unites" all regional "things" into a single codepage. However but the transition from old regional codepages to utf-8 is "difficult", because you will still need to read a file with the codepage that was used to write it.

We recommend that you "migrate" text files to utf-8 (or utf-16 if you mainly use "special" characters, like chinese). If you add a byte order mark the format becomes "visible", but in some contexts (certain Internet contexts and using foreign applications) byte order marks in utf-8 files will not be accepted.

Fallback CodePage

Several text file related predicates have been equipped with a non-optional FallbackCodePage argument. For example:

class inputStream_file : inputStream
...
constructors
    openFileText : (string Filename, codePage FallbackCodePage, fileSystem_api::accessPermit Access = fileSystem_api::permitRead).

The predicate inputStream_file::openFileText examines the file to open searching for Byte Order Marks and obvious utf-16 format. If it is not possible to determine the codepage like that the FallbackCodePage will be used. Such predicates used to user the threadAnsi codepage, but (as described above) this can lead to problems with "old" files and new settings and files written on one computer and read on another. For this reason you must now (explicitly) decide which codepage to use if it cannot be inferred.

COM component classes

The import of COM components has been simplified for certain COM components. Instead of being defined in a separate Visual Prolog class many COM components are now created by calling a predicate.

For example, to create an XMLHTTP component you would construct object using the corresponding constructor XmlHttp = xmlhttp60::new(), now you will instead call the predicate XmlHttp = msxml_HTTP_api::newXmlhttp60().

watchDirectory

watchDirectory has been updated to use suspending predicates, and as result watchDirectory::start takes an executionContext as argument. In most cases pfc\asynchronous\executionContext_pool::defaultPool can be used.

            WD = watchDirectory::start(FullDir, pfc\asynchronous\executionContext_pool::defaultPool),

Master/slave

The master/slave concept has been updated to use suspending predicates instead of explicit continuations. The article Master/slave processes and the corresponding demo example has been updated to reflect the changes.

Web Services & webSockets

The Web Services and webSockets support has been updated to use suspending predicates for asynchronous operation. The main updates necessary to make an old program working is remove all access to threadpools.

This code

clauses
    runServer() :-
        ...
        mkService(Session, "File service", "", requestQueue_content::new(threadpool, demoFileService::contentMapper)),
        ...

will look like this:

clauses
    runServer() :-
        ...
        mkService(Session, "File service", "", requestQueue_content::new(demoFileService::contentMapper)),
        ...

If necessary you can add an explicit executionContext (Ctx):

clauses
    runServer() :-
        httpServer_api::httpInitialize(httpServer_native::http_initialize_server),
        Session = session::new(),
        Ctx = pfc\asynchronous\executionContext_pool::defaultPool,
        try
            cleanup:alsoDispose(Session),
            % Demo file service for html, JavaScript , images, etc to the client
            mkService(Session, "File service", "", requestQueue_content::new(demoFileService::contentMapper, Ctx)),
            ...

The changes in webSocket requires more attention. You will have to remove continuations and use suspending predicates instead.

For example, the predicate onAttach_async must now be a suspending predicate:

interface webSocketService
 
predicates
    onAttach_async : (httpRequest Request, webSocket WebSocket) suspending.
...

The implementation of this predicate:

clauses
    onAttach_async(Request, WebSocket, Continuation) :-
        onAttach2_async(filename::getLastDirectory(Request:path, _), WebSocket, Continuation).
should drop the Continuation argument:
clauses
    onAttach_async(Request, WebSocket) :-
        onAttach2_async(filename::getLastDirectory(Request:path, _), WebSocket).

onAttach2_async should also be declared as a suspending predicate (without a continuation argument):

predicates
    onAttach2_async : (string Last, webSocket WebSocket) suspending.

The interesting part is that the real functional predicates like:

class predicates
    echoLoop_async : (webSocket WebSocket, continuation{unit} Continuation).
clauses
    echoLoop_async(WebSocket, Continuation) :-
        WebSocket:readMessage_async(
            Continuation:mkContinue(
                { (Message) :-
                    if webSocket::close(Status, Reason) = Message then
                        if 0 <> Status then
                            stdio::writef("Closed:% %\n", Status, Reason)
                        end if,
                        Continuation:success(unit)
                    else
                        if webSocket::ss(U16) = Message then
                            stdio::writef("Message:%\n", segmented_utf16::getAsString(U16))
                        end if,
                        WebSocket:writeMessage_async(Message,
                            Continuation:mkContinue(
                                {  :-
                                    echoLoop_async(WebSocket, Continuation) %
                                }))
                    end if
                })).

Should now be implemented as a suspending predicate which result in much simpler code:

class predicates
    echoLoop_async : (webSocket WebSocket) suspending.
clauses
    echoLoop_async(WebSocket) :-
        Message = WebSocket:readMessage_async(),
        if webSocket::close(Status, Reason) = Message then
            if 0 <> Status then
                stdio::writef("Closed:% %\n", Status, Reason)
            end if
        else
            if webSocket::ss(U16) = Message then
                stdio::writef("Message:%\n", segmented_utf16::getAsString(U16))
            end if,
            WebSocket:writeMessage_async(Message),
            echoLoop_async(WebSocket)
        end if.

Especially, notice that this fragment of the code:

clauses
    ...
        WebSocket:readMessage_async(
            Continuation:mkContinue(
                { (Message) :-
                    ...
                })).

simply becomes an ordinary call:

clauses
    ...
        Message = WebSocket:readMessage_async(),
        ...

See also