Skip to content

Commit

Permalink
DOC: New program initialization handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Wielemaker committed Jun 6, 2017
1 parent a637b68 commit 17d7e1f
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 32 deletions.
8 changes: 5 additions & 3 deletions library/main.pl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Author: Jan Wielemaker
E-mail: [email protected]
WWW: http://www.swi-prolog.org
Copyright (c) 2002-2016, University of Amsterdam
Copyright (c) 2002-2017, University of Amsterdam
VU University Amsterdam
All rights reserved.
Expand Down Expand Up @@ -49,7 +49,7 @@
==
#!/usr/bin/env swipl
:- initialization main.
:- initialization(main, main).
main(Argv) :-
echo(Argv).
Expand All @@ -71,7 +71,9 @@

%! main
%
% Call main/1 using the passed command-line arguments.
% Call main/1 using the passed command-line arguments. Before calling
% main/1 this predicate installs a signal handler for =SIGINT=
% (Control-C) that terminates the process with status 1.

main :-
context_module(M),
Expand Down
41 changes: 34 additions & 7 deletions man/builtin.doc
Original file line number Diff line number Diff line change
Expand Up @@ -809,13 +809,40 @@ compatibility issue.
Similar to initialization/1, but allows for specifying when \arg{Goal}
is executed while loading the program text:

\begin{description}
\termitem{now}{} Execute \arg{Goal} immediately.
\termitem{after_load}{} Execute \arg{Goal} after loading
program text. This is the same as initialization/1.
\termitem{restore}{} Do not execute \arg{Goal} while loading
the program, but \emph{only} when restoring a state.
\end{description}
\begin{description}
\termitem{now}{} Execute \arg{Goal} immediately.

\termitem{after_load}{} Execute \arg{Goal} after loading the
program text in which the directive appears. This is the same as
initialization/1.

\termitem{restore}{}
Do not execute \arg{Goal} while loading the program, but \emph{only}
when restoring a saved state.

\termitem{program}{}
Execute \arg{Goal} once after executing the \cmdlineoption{-g} goals
at program startup. Registered goals are executed in the order
encountered and a failure or exception causes the Prolog to exit with
non-zero exit status. These goals are \emph{not} executed if the
\cmdlineoption{-l} is given to merely \emph{load} files. In that case
they may be executed explicitly using initialize/0. See also
\secref{plscript}.

\termitem{main}{}
When Prolog starts, the last goal registered using
\term{initialization}{Goal, main} is executed as main goal. If
\arg{Goal} fails or raises an exception, the process terminates with
non-zero exit code. If \arg{Goal} succeeds, the process terminates
with zero exit code. See also \secref{plscript}.
\end{description}

\predicate[det]{initialization}{0}{}
Run all initialization goals registered using
\term{initialization}{Goal, program}. Raises an error
\term{initialization_error}{Reason, Goal, File:Line} if \arg{Goal}
fails or raises an exception. \arg{Reason} is \const{failed} or
the exception raised.

\predicate{compiling}{0}{}
True if the system is compiling source files with the \cmdlineoption{-c}
Expand Down
40 changes: 18 additions & 22 deletions man/overview.doc
Original file line number Diff line number Diff line change
Expand Up @@ -844,16 +844,14 @@ vs.\ Windows).
\subsubsection{Using PrologScript} \label{sec:plscript}

A Prolog source file can be used directly as a Unix program using the
Unix \verb$#!$ magic start. The same mechanism is useful for specifying
additional parameters for running a Prolog file on Windows. The Unix
\verb$#!$ magic is allowed because if the first letter of a Prolog file
is \verb$#$, the first line is treated as a comment.\footnote{The
\texttt{\#}-sign can be the legal start of a normal Prolog clause. In
the unlikely case this is required, leave the first line blank or add a
header comment.} To create a Prolog script, use one of the two
alternatives below as first line. The first can be used to bind a script
to a specific Prolog installation, while the latter uses the default
prolog installed in \verb"$PATH".
Unix \verb$#!$ magic start. The Unix \verb$#!$ magic is allowed because
if the first letter of a Prolog file is \verb$#$, the first line is
treated as a comment.\footnote{The \texttt{\#}-sign can be the legal
start of a normal Prolog clause. In the unlikely case this is required,
leave the first line blank or add a header comment.} To create a Prolog
script, use one of the two alternatives below as first line. The first
can be used to bind a script to a specific Prolog installation, while
the latter uses the default prolog installed in \verb"$PATH".

\begin{code}
!/path/to/swipl
Expand All @@ -866,26 +864,25 @@ portability, the \verb$#!$ must be followed immediately with an absolute
path to the executable and should have none or one argument. Neither the
executable path, nor the argument shall use quotes or spaces. When
started this way, the Prolog flag \prologflag{argv} contains the command
line arguments that follow the script invocation. Below is a simple
script doing expression evaluation:
line arguments that follow the script invocation.

Starting with version 7.5.8, initialization/2 support the \arg{When}
options \const{program} and \const{main}, allowing for the following
definition of a Prolog script that evaluates an arithmetic expression on
the command line. Note that main/0 is defined lib the library
\pllib{main}. It calls main/1 with the command line arguments after
disabling signal handling.

\begin{code}
#!/usr/bin/env swipl

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

eval :-
current_prolog_flag(argv, Argv),
main(Argv) :-
concat_atom(Argv, ' ', SingleArg),
term_to_atom(Term, SingleArg),
Val is Term,
format('~w~n', [Val]).

main :-
catch(eval, E, (print_message(error, E), fail)),
halt.
main :-
halt(1).
\end{code}

And here are two example runs:
Expand All @@ -895,7 +892,6 @@ And here are two example runs:
3
% ./eval foo
ERROR: is/2: Arithmetic: `foo/0' is not a function
%
\end{code}

The Windows version simply ignores the \verb$#!$ line.\footnote{Older
Expand Down
1 change: 1 addition & 0 deletions man/summary.doc
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ suggest predicates from a keyword.
\predicatesummary{include}{1}{Include a file with declarations}
\predicatesummary{initialization}{1}{Initialization directive}
\predicatesummary{initialization}{2}{Initialization directive}
\predicatesummary{initialize}{0}{Run program initialization}
\predicatesummary{instance}{2}{Fetch clause or record from reference}
\predicatesummary{integer}{1}{Type check for integer}
\predicatesummary{interactor}{0}{Start new thread with console and top level}
Expand Down

8 comments on commit 17d7e1f

@triska
Copy link
Member

@triska triska commented on 17d7e1f Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid so many uses of main (with different meanings) which are likely to cause confusion, I have one naming suggestion for the user-defined predicate in these examples: run/0.

The directives in the sample code would then become:

:- initialization(run, main).

@JanWielemaker
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do admit that initialization(main, main). can be a little confusing. The first main comes from
library(main). I'm reluctant changing this to run/0. main is the generally used name for entry
points. We could also rename the initialization type. I doubt run is better though.

@triska
Copy link
Member

@triska triska commented on 17d7e1f Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I name all such entry points run/0 in my programs. I have many, many test cases, which I all invoke like this. They run, and run, and run!

@JanWielemaker
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing stops you from using :- initialization(run, main). :) main/0 doesn't do that much. Only collecting the arguments (which is really easy) and rebind Control-C to stop the application. This whole thing has been a nuisance for quite a while to me. I'm glad it is resolved. Will update http_unix_daemon.pl accordingly, so your config + proxy should work fine.

@triska
Copy link
Member

@triska triska commented on 17d7e1f Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thank you very much!

@JanWielemaker
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Seems http_unix_daemon.pl asks the user to use initialization/1. This isn't a great advice any longer. I see two options:

  • Change the doc to suggest :- initialization(http_daemon, main).
  • Remove this from the docs and put the above in the library.

In both cases old code will keep working as it will simply call the non-returning (half functional)
:- initialization(http_daemon). In both cases you can define your own using an explicit

:- initialization(my_entry, main).

my_entry :-
     ....
     http_daemon(Options).

What do you think? It may depend on Eyal's reply. If he comes with a convincing argument to only honour main initialization from the script argument file(s), putting it inside is no option. If that doesn’t change I think my preference is to put the initialization/2 call inside the library. After all, there is no application for this library except for managing an HTTP server as an application.

@triska
Copy link
Member

@triska triska commented on 17d7e1f Jun 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the HTTP Unix daemon, I think it is particularly important to guarantee:

  1. mutatis mutandis, the following :
    $ swipl config.pl proloxy.pl
    is equivalent to:
    $ swipl proloxy.pl config.pl
  2. using --interactive and/or --no-fork yields exactly the same dispatching as not using it.

There may also be other important guarantees that are worth to ensure (and which probably even already hold now thanks to your patches), but the two above are those that come to mind immediately because they were previously not satisfied (SWI-Prolog/packages-http#71).

For backwards compatibility, it is definitely a big plus that initialization/1 as previously recommended can still be used, with the same (albeit broken) results as previously. However, if this stands in the way of more desirable guarantees, then it can as well be removed in my view, and moved into the library itself.

@JanWielemaker
Copy link
Member

@JanWielemaker JanWielemaker commented on 17d7e1f Jun 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I scan through SWI-Prolog/packages-http#71, both issues were caused by the initialization/1 goal not returning. These should be fixed using the new way to declare the entry point. I think the ways to make the program depending on the loading order remaining are:

  • Both files declare the entry point. In that case the last wins.
  • Both files add to a multifile predicate they use. In that case the clause ordering changes. That is
    the case for the registered HTTP handlers and that is why a priority is used to create a handler
    that overrules another.

I'll add the initialization to the library. This isn't all set in stone yet. If anyone comes in with a good suggesting in the next week or so, things will change.

Please sign in to comment.