For example, the following code runs in about 1 second because both sleep/1 calls happen in parallel. When foo/0 unifies L, it blocks until silly/1 has finished.
silly(L) :-
sleep(1),
L = [a,b].
foo :-
spawn(silly(L)),
sleep(1),
L=[A,B], % blocks, if necessary
writeln(A-B).
If Goal produces multiple solutions, they're iterated when
backtracking over the unification (L=[A,B] above). If Goal fails
or throws an exception, the calling thread sees it at the unification
point.
lazy thread policy.
lazy/1 can be helpful when complicated or expensive goals are only needed in some code paths but duplicating those goals is too verbose. It can be an alternative to creating a new, named predicate. For example,
foo(Xs) :-
lazy(i_am_slow(a,B,[c(C),d(d),e(etc)])), % complicated
( day_of_week(tuesday) ->
append(B,C,Xs)
; phase_of_moon(full) ->
append(C,B,Xs)
; true ->
% i_am_slow/3 not executed in this code path
Xs = [hi]
).
Options are as follows:
ephemeral (default), create a new thread in which to call
goal. If lazy, only execute Goal when await/1 is called; no
background threads are used.await/1 strives to have the same determinism as the original Goal passed to async/3. If that goal fails, await/1 fails. If that goal throws an exception, so does await/1. If that goal produces many solutions, so does await/1 on backtracking.