Did you know ... | Search Documentation: |
Pack delay -- README.md |
:- use_module(library(delay)). main :- % describe a relationship between variables. % none of them need values yet. delay(atom_codes(Atom,Codes)), % now we can bind a value... Codes = "hello", % and the other one receives a value too Atom == hello.
Many Prolog predicates (succ/2, plus/3, atom_codes/2, etc) describe a relationship between values. However, if called with variables whose values are not yet known, they throw a "not sufficiently instantiated" exception. Unfortunately, this imposes a strict order of execution and often requires one to write additional clauses that differ only in goal order.
By wrapping these predicates in delay/1, they become purely declarative. Their execution order no longer matters and happens as soon as it reasonably can.
For example, imagine a simple DCG for saying hello. We want the subject of our greeting to be specified as an atom. So we might write
hello(Whom) --> { atom_codes(Whom, Codes) }, "Hello, ", string(Codes).
That works fine if we run it forward
?- phrase(hello(john), X). X = "Hello, john".
but not if we run it backwards
?- phrase(hello(Whom), "Hello, john"). ERROR: atom_codes/2: Arguments are not sufficiently instantiated
Instead of writing a second clause for hello//1, in which the goals are reordered, we use delay/1
hello(Whom) --> { delay(atom_codes(Whom, Codes)) }, "Hello, ", string(Codes).
and now we get what we wanted
?- phrase(hello(Whom), "Hello, john"). Whom = john ; false.
library(delay)
comes with support for some built-in predicates. To
add support other predicates, define clauses for the multifile
predicate mode/1.
For example, if the module utils
exports helpful/2 and that
predicate requires at least one of its arguments to be ground, you can
add delay support with
:- multifile delay:mode/1. delay:mode(utils:helpful(ground,_)). delay:mode(utils:helpful(_,ground)).
library(delay)
first looks for mode information under the calling
module's name. If none is found, it looks for it under the exporting
module's name. The example above defines mode information for all
users of helpful/2. If your module mine
imports helpful/2
and you only want the mode declarations to have effect locally, you
can do this instead:
:- multifile delay:mode/1. delay:mode(mine:helpful(ground,_)). delay:mode(mine:helpful(_,ground)).
Don't worry about adding mode/1 declarations for predicates that already have them. Redundant mode declarations are ignored.
If you create mode declarations for built-in predicates, please consider contributing them as a pull request to this library (see below). That way, other users can benefit too.
When using delay/1, be certain to think carefully about all surrounding cuts. Delaying over a cut changes the semantics.
This library might someday offer development-time warnings to bring these to your attention. However, such a tool doesn't now exist and is unlikely to discover all circumstances in which delay and cut interact unexpectedly.
SWI-Prolog's SICSTUS compatibility layer includes the block/1 directive. That directive fills a niche very similar to delay/1.
Here are some advantages of delay/1 over block/1:
One disadvantage is that delay/1 is not compatible with existing SICSTUS code.
Using SWI-Prolog 6.3 or later:
?- pack_install(delay).
This module uses semantic versioning.
Source code is available at http://github.com/mndrix/delay