Getting an initialization error with 10.0.1

Preamble: I’ve been using swi prolog for > 20 years.

I have a top-level module that looks like this:

:- initialization top.

:- use_module(driver).

eval :-
  current_prolog_flag(argv, Argv),
  main(Argv).

top :-
  catch(eval, E, (print_message(error, E), fail)),
  halt.
top :-
  halt(1).

This used to work. But no longer does when I upgraded to 10.0.1

I am on a mac running Tahoe.

Note: the error I get is:

ERROR: [Thread main] /Users/fgm/Projects/star/src/BootCompiler/sbc.pl:1: Initialization goal raised exception:

Note: the offending exception is not displayed.

Note: the program (driver in this case) does run to completion successfully, and does not throw an exception (which I have verified by running interactively)

Suggestions would be welcome. I tried searching both with and without ‘ai’ and no luck.

Cheers,
Francis

This seems to have something to do with the :- initialization directive. I tried using initialization/2 with different second argument. Here is what I see:

:- initialization(top, now).

Initialization goal raised exception: ERROR: '$run_init_goal'/1: Unknown procedure: top/0

:- initialization(top, after_load).

ERROR: [Thread main] Initialization goal raised exception:

(and nothing else, as you reported; this is also documented to be “same as initialization/1”)

Using main or program as the second argument seems to circumvent the problem.

Of course, the error message is misleading. The correct solution is indeed to use initialization/2, preferably with type main. Typically you want to combine that with library(main), which provides Ctrl-C handling and option parsing.

Let me explain what is wrong with initialization/1.

initialization/1 is executed after loading a file. This implies that if you call initialization/1 to run the entry point, you can only do so from the “main” file. Embedding this goal in some indirectly loaded file will cause it to executed before all code is loaded.

The second problem is that SWI-Prolog loads files while protecting against interrupts. This includes running the initialization goals. This is needed to avoid partial lazy loading due to interrupts/timeout.

IMO, initialization/1 only makes sense to perform preparation steps that cannot be part of the compilation process itself.

SWI-Prolog introduced initialization/2 to give more control over when the initialization is performed. Notably :- initialization(mygoal, main). will run the entry point. This is executed after all code is loaded and the interrupt protection is released. The nice thing of this is also that the entry point is not executed if

  • The file was loaded with swipl -l file.pl ...
  • The file was loaded from the interactive toplevel.

This makes debugging and running comfortable. To run, use

 swipl script.pl <script arg ...>

To debug, run

swipl -l script.pl <script arg ...>

Setup your debugging and call the entry point from the toplevel.

I’ve changed the message to

> src/swipl top.pl aap 
Hello aap
Warning: [Thread main] /home/jan/src/swipl-devel/build/top.pl:1: top
Warning: [Thread main]   Initialization goal called halt(0).
Warning: [Thread main]   The program entry point should be called using initialization/2.
Warning: [Thread main]   Consider using library(main).

The problem is the non-standard behaviour. Alternatively we could opt to make the initialization goals for the toplevel script special? I’m afraid that will be a bit fragile though.

Suggestions?

1 Like

Thank you for the guidance.

Has this changed recently?

The fact that you get a misleading error is relatively new. This change was caused by making halt/0 work using an exception and was introduced somewhere in the 9.3.x devel series. The issue that running the entry point from initialization suffers from interrupt issues is much older.

Dear Francis,

This is a bit of side note.
If you do a lot of command line interactions with Prolog programs and scripts, you might want to have a look at pack upsh "upsh" pack for SWI-Prolog .

I just published v3.0 there is a message on the forum.

Regards,

Nicos Angelopoulos

Thanks,

I’ll definitely take a look.

Francis

I’m curious what capababilities this pack provides beyond what can be done with

#!/usr/bin/env -S swipl -q

:- initialization(main, main).
main(Argv) :- ...

(swipl recognizes the special comment #!, so consult/1 and command line loading work fine)

Dear Peter,

I posted on the forum a message with the main features, such as locating scripts, auto selection of scripts, entry point flexibility and translating terms. I hope the post gives some idea.

Historically, Upsh has been around a long time, not sure if it predates the use of #! in SWI. It definitely supported that. Now days, I find no reason to add #! to my pl files. It also supported 3 different systems, so it was an extra layer of abstraction. I do a lot of cli work and convenience is my main motivation. Maybe is not so much the case what cannot be done by other means, but how convenient is to use on a day-to-day basis.

Regards,

Nicos

@nicos can probably elaborate. It mainly seems to support finding scripts and make argument processing easier by providing a Prolog friendly default conversion.

Note that alternatively you can use SWI-Prolog’s app infrastructure. An app is a .pl file that is located in the app directory of Prolog (containing e.g., qlf and pack), the app directory of an installed pack or the app directory as subdirectory of the same directory where it searches for init.pl (On non-Windows ~/.local/share/swi-prolog/app).

The app infrastructure is just about locating scripts and adjusting argument processing such that the overall syntax is

 swipl [prolog flags] app [app arguments]

The app itself should use initialization/2 and whatever argument processing it wishes to do, i.e., it is simply loaded as a normal Prolog file. It was inspired by Python’s python -m module [arg ...], providing a uniform and portable mechanism for running Prolog scripts.