pengines.pl -- Pengines: Web Logic Programming Made Easy
The library(pengines) provides an infrastructure for creating Prolog engines in a (remote) pengine server and accessing these engines either from Prolog or JavaScript.
- pengine_create(:Options) is det
- Creates a new pengine. Valid options are:
- id(-ID)
- ID gets instantiated to the id of the created pengine. ID is atomic.
- alias(+Name)
- The pengine is named Name (an atom). A slave pengine (child) can subsequently be referred to by this name.
- application(+Application)
- Application in which the pengine runs. See pengine_application/1.
- server(+URL)
- The pengine will run in (and in the Prolog context of) the pengine server located at URL.
- src_list(+List_of_clauses)
- Inject a list of Prolog clauses into the pengine.
- src_text(+Atom_or_string)
- Inject the clauses specified by a source text into the pengine.
- src_url(+URL)
- Inject the clauses specified in the file located at URL into the pengine.
- src_predicates(+List)
- Send the local predicates denoted by List to the remote pengine. List is a list of predicate indicators.
Remaining options are passed to http_open/3 (meaningful only for non-local pengines) and thread_create/3. Note that for thread_create/3 only options changing the stack-sizes can be used. In particular, do not pass the detached or alias options..
Successful creation of a pengine will return an event term of the following form:
- create(ID, Term)
- ID is the id of the pengine that was created. Term is not used at the moment.
An error will be returned if the pengine could not be created:
- error(ID, Term)
- ID is invalid, since no pengine was created. Term is the exception's error term.
- pengine_ask(+NameOrID, @Query, +Options) is det
- Asks pengine NameOrID a query Query.
Options is a list of options:
- template(+Template)
- Template is a variable (or a term containing variables) shared with the query. By default, the template is identical to the query.
- chunk(+IntegerOrFalse)
- Retrieve solutions in chunks of Integer rather than one by one. 1
means no chunking (default). Other integers indicate the maximum
number of solutions to retrieve in one chunk. If
false
, the Pengine goal is not executed using findall/3 and friends and we do not backtrack immediately over the goal. As a result, changes to backtrackable global state are retained. This is similar that usingset_prolog_flag(toplevel_mode, recursive)
. - bindings(+Bindings)
- Sets the global variable '$variable_names' to a list of
Name = Var
terms, providing access to the actual variable names.
Any remaining options are passed to pengine_send/3.
Note that the predicate pengine_ask/3 is deterministic, even for queries that have more than one solution. Also, the variables in Query will not be bound. Instead, results will be returned in the form of event terms.
- success(ID, Terms, Projection, Time, More)
- ID is the id of the pengine that succeeded in solving the query.
Terms is a list holding instantiations of Template. Projection
is a list of variable names that should be displayed. Time is
the CPU time used to produce the results and finally, More
is either
true
orfalse
, indicating whether we can expect the pengine to be able to return more solutions or not, would we call pengine_next/2. - failure(ID)
- ID is the id of the pengine that failed for lack of a solutions.
- error(ID, Term)
- ID is the id of the pengine throwing the exception. Term is the exception's error term.
- output(ID, Term)
- ID is the id of a pengine running the query that called pengine_output/1. Term is the term that was passed in the first argument of pengine_output/1 when it was called.
- prompt(ID, Term)
- ID is the id of the pengine that called pengine_input/2 and Term is the prompt.
Defined in terms of pengine_send/3, like so:
pengine_ask(ID, Query, Options) :- partition(pengine_ask_option, Options, AskOptions, SendOptions), pengine_send(ID, ask(Query, AskOptions), SendOptions).
- pengine_next(+NameOrID, +Options) is det
- Asks pengine NameOrID for the next solution to a query started by
pengine_ask/3. Defined options are:
- chunk(+Count)
- Modify the chunk-size to Count before asking the next set of
solutions. This may not be used if the goal was started with
chunk(false)
.
Remaining options are passed to pengine_send/3. The result of re-executing the current goal is returned to the caller's message queue in the form of event terms.
- success(ID, Terms, Projection, Time, More)
- See pengine_ask/3.
- failure(ID)
- ID is the id of the pengine that failed for lack of more solutions.
- error(ID, Term)
- ID is the id of the pengine throwing the exception. Term is the exception's error term.
- output(ID, Term)
- ID is the id of a pengine running the query that called pengine_output/1. Term is the term that was passed in the first argument of pengine_output/1 when it was called.
- prompt(ID, Term)
- ID is the id of the pengine that called pengine_input/2 and Term is the prompt.
Defined in terms of pengine_send/3, as follows:
pengine_next(ID, Options) :- pengine_send(ID, next, Options).
- pengine_stop(+NameOrID, +Options) is det
- Tells pengine NameOrID to stop looking for more solutions to a query
started by pengine_ask/3. Options are passed to pengine_send/3.
Defined in terms of pengine_send/3, like so:
pengine_stop(ID, Options) :- pengine_send(ID, stop, Options).
- pengine_abort(+NameOrID) is det
- Aborts the running query. The pengine goes back to state `2', waiting for new queries.
- pengine_destroy(+NameOrID) is det
- pengine_destroy(+NameOrID, +Options) is det
- Destroys the pengine NameOrID. With the option
force(true)
, the pengine is killed using abort/0 and pengine_destroy/2 succeeds. - pengine_self(-Id) is det
- True if the current thread is a pengine with Id.
- pengine_application(+Application) is det
- Directive that must be used to declare a pengine application module. The
module must not be associated to any file. The default application is
pengine_sandbox
. The example below creates a new applicationaddress_book
and imports the API defined in the module fileadress_book_api.pl
into the application.:- pengine_application(address_book). :- use_module(address_book:adress_book_api).
- current_pengine_application(?Application) is nondet
- True when Application is a currently defined application.
- pengine_property(?Pengine, ?Property) is nondet
- True when Property is a property of the given Pengine. Enumerates all
pengines that are known to the calling Prolog process. Defined
properties are:
- self(ID)
- Identifier of the pengine. This is the same as the first argument, and can be used to enumerate all known pengines.
- alias(Name)
- Name is the alias name of the pengine, as provided through the
alias
option when creating the pengine. - thread(Thread)
- If the pengine is a local pengine, Thread is the Prolog thread identifier of the pengine.
- remote(Server)
- If the pengine is remote, the URL of the server.
- application(Application)
- Pengine runs the given application
- module(Module)
- Temporary module used for running the Pengine.
- destroy(Destroy)
- Destroy is
true
if the pengines is destroyed automatically after completing the query. - parent(Queue)
- Message queue to which the (local) pengine reports.
- source(?SourceID, ?Source)
- Source is the source code with the given SourceID. May be present if
the setting
debug_info
is present. - detached(?Time)
- Pengine was detached at Time.
- pengine_output(+Term) is det
- Sends Term to the parent pengine or thread.
- pengine_debug(+Format, +Args) is det
- Create a message using format/3 from Format and Args and send this to
the client. The default JavaScript client will call
console.log(Message)
if there is a console. The predicate pengine_rpc/3 callsdebug(pengine(debug), '~w', [Message])
. The debug topicpengine(debug)
is enabled by default. - thread_pool:create_pool(+Application) is det[multifile]
- On demand creation of a thread pool for a pengine application.
- pengine_done is det
- Called from the pengine thread
at_exit
option. Destroys child pengines using pengine_destroy/1. Cleaning up the Pengine is synchronised by thepengine_done
mutex. See read_event/6. - prepare_module(+Module, +Application, +Options) is semidet[multifile]
- Hook, called to initialize the temporary private module that
provides the working context of a pengine. This hook is executed
by the pengine's thread. Preparing the source consists of three
steps:
- Add Application as (first) default import module for Module
- Call this hook
- Compile the source provided by the the
src_text
andsrc_url
options
- prepare_goal(+Goal0, -Goal1, +Options) is semidet[multifile]
- Pre-preparation hook for running Goal0. The hook runs in the context of the pengine. Goal is the raw goal given to ask. The returned Goal1 is subject to goal expansion (expand_goal/2) and sandbox validation (safe_goal/1) prior to execution. If this goal fails, Goal0 is used for further processing.
- not_sandboxed(+User, +Application) is semidet[multifile]
- This hook is called to see whether the Pengine must be executed in a
protected environment. It is only called after authentication_hook/3
has confirmed the authentity of the current user. If this hook
succeeds, both loading the code and executing the query is executed
without enforcing sandbox security. Typically, one should:
- Provide a safe user authentication hook.
- Enable HTTPS in the server or put it behind an HTTPS proxy and ensure that the network between the proxy and the pengine server can be trusted.
- pengine_pull_response(+Pengine, +Options) is det
- Pulls a response (an event term) from the slave Pengine if Pengine is a remote process, else does nothing at all.
- pengine_input(+Prompt, -Term) is det
- Sends Prompt to the master (parent) pengine and waits for input. Note that Prompt may be any term, compound as well as atomic.
- pengine_respond(+Pengine, +Input, +Options) is det
- Sends a response in the form of the term Input to a slave (child) pengine
that has prompted its master (parent) for input.
Defined in terms of pengine_send/3, as follows:
pengine_respond(Pengine, Input, Options) :- pengine_send(Pengine, input(Input), Options).
- pengine_event_loop(:Closure, +Options) is det
- Starts an event loop accepting event terms sent to the current pengine
or thread. For each such event E, calls
ignore(call(Closure, E))
. A closure thus acts as a handler for the event. Some events are also treated specially:- create(ID, Term)
- The ID is placed in a list of active pengines.
- destroy(ID)
- The ID is removed from the list of active pengines. When the last pengine ID is removed, the loop terminates.
- output(ID, Term)
- The predicate pengine_pull_response/2 is called.
Valid options are:
- autoforward(+To)
- Forwards received event terms to slaves. To is either
all
,all_but_sender
or a Prolog list of NameOrIDs. [not yet implemented]
- pengine_rpc(+URL, +Query) is nondet
- pengine_rpc(+URL, +Query, +Options) is nondet
- Semantically equivalent to the sequence below, except that the query is
executed in (and in the Prolog context of) the pengine server referred
to by URL, rather than locally.
copy_term_nat(Query, Copy), % attributes are not copied to the server call(Copy), % executed on server at URL Query = Copy.
Valid options are:
- chunk(+IntegerOrFalse)
- Can be used to reduce the number of network roundtrips being made. See pengine_ask/3.
- timeout(+Time)
- Wait at most Time seconds for the next event from the server.
The default is defined by the setting
pengines:time_limit
.
Remaining options (except the server option) are passed to pengine_create/1.
- prompt(+ID, +Prompt, -Term) is semidet[multifile]
- Hook to handle pengine_input/2 from the remote pengine. If the hooks fails, pengine_rpc/3 calls read/1 using the current prompt.
- output(+ID, +Term) is semidet[multifile]
- Hook to handle pengine_output/1 from the remote pengine. If the hook fails, it calls print/1 on Term.
- portray_blob(+Blob, +Options) is det
- Portray non-text blobs that may appear in output terms. Not
really sure about that. Basically such terms need to be avoided
as they are meaningless outside the process. The generated error
is hard to debug though, so now we send them as
'$BLOB'(Type)
. Future versions may include more info, depending on Type. - write_result(+Lang, +Event, +Dict) is semidet[multifile]
- Hook that allows for different output formats. The core Pengines
library supports
prolog
and various JSON dialects. The hook event_to_json/3 can be used to refine the JSON dialects. This hook must be used if a completely different output format is desired. - add_error_details(+Error, +JSON0, -JSON)
- Add format error code and location information to an error. Also used by pengines_io.pl.
- event_to_json(+Event, -JSONTerm, +Lang) is semidet[multifile]
- Hook that translates a Pengine event structure into a term suitable
for reply_json/1, according to the language specification Lang. This
can be used to massage general Prolog terms, notably associated with
success(ID, Bindings, Projection, Time, More)
andoutput(ID, Term)
into a format suitable for processing at the client side. - authentication_hook(+Request, +Application, -User) is semidet[multifile]
- This hook is called from the =/pengine/create= HTTP handler to
discover whether the server is accessed by an authorized user. It
can react in three ways:
- Succeed, binding User to a ground term. The authentity of the user is available through pengine_user/1.
- Fail. The =/create= succeeds, but the pengine is not associated with a user.
- Throw an exception to prevent creation of the pengine. Two
meaningful exceptions are:
throw(http_reply(authorise(basic(Realm))))
Start a normal HTTP login challenge (reply 401)throw(http_reply(forbidden(Path)))
) Reject the request using a 403 repply.
- pengine_user(-User) is semidet
- True when the pengine was create by an HTTP request that authorized User.
- pengine_event(?EventTerm) is det
- pengine_event(?EventTerm, +Options) is det
- Examines the pengine's event queue and if necessary blocks execution
until a term that unifies to Term arrives in the queue. After a term
from the queue has been unified to Term, the term is deleted from the
queue.
Valid options are:
- timeout(+Time)
- Time is a float or integer and specifies the maximum time to wait
in seconds. If no event has arrived before the time is up EventTerm
is bound to the atom
timeout
. - listen(+Id)
- Only listen to events from the pengine identified by Id.
Re-exported predicates
The following predicates are exported from this file while their implementation is defined in imported modules or non-module files loaded by this module.
- pengine_destroy(+NameOrID) is det
- pengine_destroy(+NameOrID, +Options) is det
- Destroys the pengine NameOrID. With the option
force(true)
, the pengine is killed using abort/0 and pengine_destroy/2 succeeds. - pengine_rpc(+URL, +Query) is nondet
- pengine_rpc(+URL, +Query, +Options) is nondet
- Semantically equivalent to the sequence below, except that the query is
executed in (and in the Prolog context of) the pengine server referred
to by URL, rather than locally.
copy_term_nat(Query, Copy), % attributes are not copied to the server call(Copy), % executed on server at URL Query = Copy.
Valid options are:
- chunk(+IntegerOrFalse)
- Can be used to reduce the number of network roundtrips being made. See pengine_ask/3.
- timeout(+Time)
- Wait at most Time seconds for the next event from the server.
The default is defined by the setting
pengines:time_limit
.
Remaining options (except the server option) are passed to pengine_create/1.