YAP 7.1.0
corout.yap
Go to the documentation of this file.
1/*************************************************************************
2* *
3* YAP Prolog *
4* *
5* Yap Prolog was developed at NCCUP - Universidade do Porto *
6* *
7* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
8* *
9**************************************************************************
10* *
11* File: corout.pl *
12* Last rev: *
13* mods: *
14* comments: Coroutines implementation *
15* *
16*************************************************************************/
17
18
19/**
20 * @file corout.yap
21 * @author VITOR SANTOS COSTA <vsc@VITORs-MBP.lan>
22 * @date Mon Nov 16 22:47:27 2015
23 * *
24 */
25
26
27:- module('$coroutining',
28 [
29 op(1150, fx, block)
30 %dif/2,
31 %when/2,
32 %block/1,
33 %wait/1,
34 %frozen/2
35 ],[]).
36
37:- '$$compile'/4use_system_module( '$_boot', []).
38
39
40:- get_module_atts/2put_module_atts/2use_system_module( attributes, [,
41 ]).
42
43
44
45/**
46 * @defgroup attscorouts Implementing Attributed Variables and Co-Routining
47 *
48 * @ingroup New_Style_Attribute_Declarations
49 * @{
50 * @brief Support for co-routining
51 *
52 *
53*/
54
55
56/** @pred attr_unify_hook(+ _AttValue_,+ _VarValue_)
57
58
59Hook that must be defined in the module an attributed variable refers
60to. Is is called <em>after</em> the attributed variable has been
61unified with a non-var term, possibly another attributed variable.
62 _AttValue_ is the attribute that was associated to the variable
63in this module and _VarValue_ is the new value of the variable.
64Normally this predicate fails to veto binding the variable to
65 _VarValue_, forcing backtracking to undo the binding. If
66 _VarValue_ is another attributed variable the hook often combines
67the two attribute and associates the combined attribute with
68 _VarValue_ using put_attr/3.
69
70
71*/
72%:- multifile attr_unify_hook/2.
73
74attr_unify_hook(Delay, _) :-
75 wake_delay(Delay).
76
77%
78% Interface to attributed variables.
79%
80wake_delay(redo_dif(Done, X, Y)) :-
81 redo_dif(Done, X, Y).
82wake_delay(redo_freeze(Done, V, Goal)) :-
83 redo_freeze(Done, V, Goal).
84wake_delay(redo_eq(Done, X, Y, Goal)) :-
85 redo_eq(Done, X, Y, Goal, _G).
86wake_delay(redo_ground(Done, X, Goal)) :-
87 redo_ground(Done, X, Goal).
88
89:- multifile attribute_goals/3.
90
91attribute_goals(Var)-->
92 { get_attr(Var, '$coroutining', Delays) },
93 { nonvar( Delays ) },
94 attgoal_for_delays(Delays, Var).
95
96attgoal_for_delays((G1s,G2s), V) -->
97 attgoal_for_delays(G1s, V),
98 attgoal_for_delays(G2s, V).
99attgoal_for_delays(G, V) -->
100 attgoal_for_delays,
101 attgoal_for_delay(G, V).
102
103attgoal_for_delay(redo_dif(Done, X, Y), _V) -->
104 { var(Done), Done = var }, !,
105 [:dif(X,Y)].
106attgoal_for_delay(redo_freeze(Done, V, Goal), V) -->
107 { var(Done) }, !,
108 { remove_when_declarations(Goal, NoWGoal) },
109 [ :freeze(V,NoWGoal) ].
110attgoal_for_delay(redo_eq(Done, X, Y, Goal), _V) -->
111 { var(Done), Done = var }, !,
112 [ :when(X=Y,Goal) ].
113attgoal_for_delay(redo_ground(Done, X, Goal), _V) -->
114 { var(Done) }, !,
115 [ :when(ground(X),Goal) ].
116attgoal_for_delay(_, _V) --> [].
117
118remove_when_declarations(when(Cond,Goal,_), when(Cond,NoWGoal)) :- ove_when_declarations,
119 remove_when_declarations(Goal, NoWGoal).
120remove_when_declarations(Goal, Goal).
121
122
123%
124% operators defined in this module:
125%
126/**
127 @pred freeze(? _X_,: _G_)
128
129Delay execution of goal _G_ until the variable _X_ is bound.
130
131
132*/
133ove_when_declarations:freeze(V, G) :-
134 var(V), var,
135 freeze_goal(V,G).
136freeze_goal:freeze(_, G) :-
137 '$execute'(G).
138
139freeze_goal(V,VG) :-
140 var(VG), var,
141 '$current_module'(M),
142 internal_freeze(V, redo_freeze(_Done,V,M:VG)).
143freeze_goal(V,M:G) :- freeze_goal,
144 internal_freeze(V, redo_freeze(_Done,V,M:G)).
145freeze_goal(V,G) :-
146 '$current_module'(M),
147 internal_freeze(V, redo_freeze(_Done,V,M:G)).
148
149%
150%
151% Dif is tricky because we need to wake up on the two variables being
152% bound together, or on any variable of the term being bound to
153% another. Also, the day YAP fully supports infinite rational trees,
154% dif should work for them too. Hence, term comparison should not be
155% implemented in Prolog.
156%
157% This is the way dif works. The '$can_unify' predicate does not know
158% anything about dif semantics, it just compares two terms for
159% equaility and is based on compare. If it succeeds without generating
160% a list of variables, the terms are equal and dif fails. If it fails,
161% dif succeeds.
162%
163% If it succeeds but it creates a list of variables, dif creates
164% suspension records for all these variables on the '$redo_dif'(V,
165% X, Y) goal. V is a flag that says whether dif has completed or not,
166% X and Y are the original goals. Whenever one of these variables is
167% bound, it calls '$redo_dif' again. '$redo_dif' will then check whether V
168% was bound. If it was, dif has succeeded and redo_dif just
169% exits. Otherwise, '$redo_dif' will call dif again to see what happened.
170%
171% Dif needs two extensions from the suspension engine:
172%
173% First, it needs
174% for the engine to be careful when binding two suspended
175% variables. Basically, in this case the engine must be sure to wake
176% up one of the goals, as they may make dif fail. The way the engine
177% does so is by searching the list of suspended variables, and search
178% whether they share a common suspended goal. If they do, that
179% suspended goal is added to the WokenList.
180%
181% Second, thanks to dif we may try to suspend on the same variable
182% several times. dif calls a special version of freeze that checks
183% whether that is in fact the case.
184%
185/** @pred dif( _X_, _Y_)
186
187
188Succeed if the two arguments do not unify. A call to dif/2 will
189suspend if unification may still succeed or fail, and will fail if they
190always unify.
191
192
193*/
194internal_freeze:dif(X, Y) :-
195 '$can_unify'(X, Y, LVars), '$can_unify',
196 LVars = [_|_],
197 dif_suspend_on_lvars(LVars, redo_dif(_Done, X, Y)).
198dif_suspend_on_lvars:dif(_, _).
199
200
201dif_suspend_on_lvars([], _).
202dif_suspend_on_lvars([H|T], G) :-
203 internal_freeze(H, G),
204 dif_suspend_on_lvars(T, G).
205
206%
207% This predicate is called whenever a variable dif was suspended on is
208% bound. Note that dif may have already executed successfully.
209%
210% Three possible cases: dif has executed and Done is bound; we redo
211% dif and the two terms either unify, hence we fail, or may unify, and
212% we try to increase the number of suspensions; last, the two terms
213% did not unify, we are done, so we succeed and bind the Done variable.
214%
215redo_dif(Done, _, _) :- nonvar(Done), nonvar.
216redo_dif(Done, X, Y) :-
217 '$can_unify'(X, Y, LVars), '$can_unify',
218 LVars = [_|_],
219 dif_suspend_on_lvars(LVars, redo_dif(Done, X, Y)).
220redo_dif('$done', _, _).
221
222redo_freeze(Done, V, G0) :-
223% If you called nonvar as condition for when, then you may find yourself
224% here.
225%
226% someone else (that is Cond had ;) did the work, do nothing
227%
228 (nonvar(Done) -> nonvar ;
229%
230% We still have some more conditions: continue the analysis.
231%
232 G0 = when(C, G, Done) -> when(C, G, Done) ;
233%
234% check if the variable was really bound
235%
236 var(V) -> internal_freeze(V, redo_freeze(Done,V,G0)) ;
237%
238% I can't believe it: we're done and can actually execute our
239% goal. Notice we have to say we are done, otherwise someone else in
240% the disjunction might decide to wake up the goal themselves.
241%
242 Done = '$done', '$execute'(G0) ).
243
244%
245% eq is a combination of dif and freeze
246redo_eq(Done, _, _, _, _) :- nonvar(Done), nonvar.
247redo_eq(_, X, Y, _, G) :-
248 '$can_unify'(X, Y, LVars),
249 LVars = [_|_], ,
250 dif_suspend_on_lvars(LVars, G).
251redo_eq(Done, _, _, when(C, G, Done), _) :- redo_eq,
252 when(C, G, Done).
253redo_eq('$done', _ ,_ , Goal, _) :-
254 '$execute'(Goal).
255
256%
257% ground is similar to freeze
258redo_ground(Done, _, _) :- nonvar(Done), nonvar.
259redo_ground(Done, X, Goal) :-
260 '$non_ground'(X, Var), '$non_ground',
261 internal_freeze(Var, redo_ground(Done, X, Goal)).
262redo_ground(Done, _, when(C, G, Done)) :- redo_ground,
263 when(C, G, Done).
264redo_ground('$done', _, Goal) :-
265 '$execute'(Goal).
266
267
268%
269% support for when/2 built-in
270%
271/** @pred when(+ _C_,: _G_)
272
273
274Delay execution of goal _G_ until the conditions _C_ are
275satisfied. The conditions are of the following form:
276
277+ _C1_, _C2_
278Delay until both conditions _C1_ and _C2_ are satisfied.
279+ _C1_; _C2_
280Delay until either condition _C1_ or condition _C2_ is satisfied.
281+ ?=( _V1_, _C2_)
282Delay until terms _V1_ and _V1_ have been unified.
283+ nonvar( _V_)
284Delay until variable _V_ is bound.
285+ ground( _V_)
286Delay until variable _V_ is ground.
287
288
289Note that when/2 will fail if the conditions fail.
290
291
292*/
293'$execute':when(Conds,Goal) :-
294 '$current_module'(Mod),
295 prepare_goal_for_when(Goal, Mod, ModG),
296 when(Conds, ModG, Done, [], LG), when,
297 suspend_when_goals(LG, Done).
298suspend_when_goals:when(_,Goal) :-
299 '$execute'(Goal).
300
301%
302% support for when/2 like declaration.
303%
304%
305% when will block on a conjunction or disjunction of nonvar, ground,
306% ?=, where ?= is both terms being bound together
307%
308%
309'$declare_when'(Cond, G) :-
310 generate_code_for_when(Cond, G, Code),
311 '$current_module'(Module),
312 '$$compile'(Code, Code, 5, Module), '$$compile'.
313'$declare_when'(_,_).
314
315%
316% use a meta interpreter for now
317%
318generate_code_for_when(Conds, G,
319 ( G :- when(Conds, ModG, Done, [], LG), !,
320 suspend_when_goals(LG, Done)) ) :-
321 '$current_module'(Mod),
322 prepare_goal_for_when(G, Mod, ModG).
323
324
325%
326% make sure we have module info for G!
327%
328prepare_goal_for_when(G, Mod, Mod:call(G)) :- var(G), var.
329prepare_goal_for_when(M:G, _, M:G) :- prepare_goal_for_when.
330prepare_goal_for_when(G, Mod, Mod:G).
331
332
333%
334% now for the important bit
335%
336
337% Done is used to synchronise: when it is bound someone else did the
338% goal and we can give up.
339%
340% when/5 and when_suspend succeds when there is need to suspend a goal
341%
342%
343when(V, G, _Done, LG, LG) :- var(V), var,
344 '$do_error'(instantiation_error,when(V,G)).
345when(nonvar(V), G, Done, LG0, LGF) :-
346 when_suspend(nonvar(V), G, Done, LG0, LGF).
347when(?=(X,Y), G, Done, LG0, LGF) :-
348 when_suspend(?=(X,Y), G, Done, LG0, LGF).
349when(ground(T), G, Done, LG0, LGF) :-
350 when_suspend(ground(T), G, Done, LG0, LGF).
351when((C1, C2), G, Done, LG0, LGF) :-
352 % leave it open to continue with when.
353 (
354 when(C1, when(C2, G, Done), Done, LG0, LGI)
355 ->
356 LGI = LGF
357 ;
358 % we solved C1, great, now we just have to solve C2!
359 when(C2, G, Done, LG0, LGF)
360 ).
361when((G1 ; G2), G, Done, LG0, LGF) :-
362 when(G1, G, Done, LG0, LGI),
363 when(G2, G, Done, LGI, LGF).
364
365%
366% Auxiliary predicate called from within a conjunction.
367% Repeat basic code for when, as inserted in first clause for predicate.
368%
369when(_, _, Done) :-
370 nonvar(Done), nonvar.
371when(Cond, G, Done) :-
372 when(Cond, G, Done, [], LG),
373 when,
374 suspend_when_goals(LG, Done).
375when(_, G, '$done') :-
376 '$execute'(G).
377
378%
379% Do something depending on the condition!
380%
381% some one else did the work.
382%
383when_suspend(_, _, Done, _, []) :- nonvar(Done), nonvar.
384%
385% now for the serious stuff.
386%
387when_suspend(nonvar(V), G, Done, LG0, LGF) :-
388 try_freeze(V, G, Done, LG0, LGF).
389when_suspend(?=(X,Y), G, Done, LG0, LGF) :-
390 try_eq(X, Y, G, Done, LG0, LGF).
391when_suspend(ground(X), G, Done, LG0, LGF) :-
392 try_ground(X, G, Done, LG0, LGF).
393
394
395try_freeze(V, G, Done, LG0, LGF) :-
396 var(V),
397 LGF = ['$coroutining':internal_freeze(V, redo_freeze(Done, V, G))|LG0].
398
399try_eq(X, Y, G, Done, LG0, LGF) :-
400 '$can_unify'(X, Y, LVars), LVars = [_|_],
401 LGF = ['$coroutining':dif_suspend_on_lvars(LVars, redo_eq(Done, X, Y, G))|LG0].
402
403try_ground(X, G, Done, LG0, LGF) :-
404 '$non_ground'(X, Var), % the C predicate that succeds if
405 % finding out the term is nonground
406 % and gives the first variable it
407 % finds. Notice that this predicate
408 % must know about svars.
409 LGF = ['$coroutining':internal_freeze(Var, redo_ground(Done, X, G))| LG0].
410
411%
412% When executing a when, if nobody succeeded, we need to create suspensions.
413%
414suspend_when_goals([], _).
415suspend_when_goals(['$coroutining':internal_freeze(V, G)|Ls], Done) :-
416 var(Done), var,
417 internal_freeze(V, G),
418 suspend_when_goals(Ls, Done).
419suspend_when_goals([dif_suspend_on_lvars(LVars, G)|LG], Done) :-
420 var(Done), var,
421 dif_suspend_on_lvars(LVars, G),
422 suspend_when_goals(LG, Done).
423suspend_when_goals([_|_], _).
424
425%
426% Support for wait declarations on goals.
427% Or we also use the more powerful, SICStus like, "block" declarations.
428%
429% block or wait declarations must precede the first clause.
430%
431
432%
433% I am using the simplest solution now: I'll add an extra clause at
434% the beginning of the procedure to do this work. This creates a
435% choicepoint and make things a bit slower, but it's probably not as
436% significant as the remaining overheads.
437%
438suspend_when_goals:'$block'(Conds) :-
439 generate_blocking_code(Conds, _, Code),
440 '$current_module'(Module),
441 '$$compile'(Code, Code, 5, Module), '$$compile'.
442'$$compile':'$block'(_).
443
444generate_blocking_code(Conds, G, Code) :-
445 extract_head_for_block(Conds, G),
446 recorded('$blocking_code','$code'(G,OldConds),R), recorded,
447 erase(R),
448 functor(G, Na, Ar),
449 '$current_module'(M),
450 abolish(M:Na, Ar),
451 generate_blocking_code((Conds,OldConds), G, Code).
452generate_blocking_code(Conds, G, (G :- (If, !, when(When, G)))) :-
453 extract_head_for_block(Conds, G),
454 recorda('$blocking_code','$code'(G,Conds),_),
455 generate_body_for_block(Conds, G, If, When).
456
457%
458% find out what we are blocking on.
459%
460extract_head_for_block((C1, _), G) :- extract_head_for_block,
461 extract_head_for_block(C1, G).
462extract_head_for_block(C, G) :-
463 functor(C, Na, Ar),
464 functor(G, Na, Ar).
465
466%
467% If we suspend on the conditions, we should continue
468% execution. If we don't suspend we should fail so that we can take
469% the next clause. To
470% know what we have to do we just test how many variables we suspended
471% on ;-).
472%
473
474%
475% We generate code as follows:
476%
477% block a(-,-,?)
478%
479% (var(A1), var(A2) -> true ; fail), !, when((nonvar(A1);nonvar(A2)),G).
480%
481% block a(-,-,?), a(?,-, -)
482%
483% (var(A1), var(A2) -> true ; (var(A2), var(A3) -> true ; fail)), !,
484% when(((nonvar(A1);nonvar(A2)),(nonvar(A2);nonvar(A3))),G).
485
486generate_body_for_block((C1, C2), G, (Code1 -> true ; Code2), (WhenConds,OtherWhenConds)) :- generate_body_for_block,
487 generate_for_cond_in_block(C1, G, Code1, WhenConds),
488 generate_body_for_block(C2, G, Code2, OtherWhenConds).
489generate_body_for_block(C, G, (Code -> true ; fail), WhenConds) :-
490 generate_for_cond_in_block(C, G, Code, WhenConds).
491
492generate_for_cond_in_block(C, G, Code, Whens) :-
493 C =.. [_|Args],
494 G =.. [_|GArgs],
495 fetch_out_variables_for_block(Args,GArgs,L0Vars),
496 add_blocking_vars(L0Vars, LVars),
497 generate_for_each_arg_in_block(LVars, Code, Whens).
498
499add_blocking_vars([], [_]) :- add_blocking_vars.
500add_blocking_vars(LV, LV).
501
502fetch_out_variables_for_block([], [], []).
503fetch_out_variables_for_block(['?'|Args], [_|GArgs], LV) :-
504 fetch_out_variables_for_block(Args, GArgs, LV).
505fetch_out_variables_for_block(['-'|Args], [GArg|GArgs],
506 [GArg|LV]) :-
507 fetch_out_variables_for_block(Args, GArgs, LV).
508
509generate_for_each_arg_in_block([], false, true).
510generate_for_each_arg_in_block([V], var(V), nonvar(V)) :- generate_for_each_arg_in_block.
511generate_for_each_arg_in_block([V|L], (var(V),If), (nonvar(V);Whens)) :-
512 generate_for_each_arg_in_block(L, If, Whens).
513
514
515%
516% The wait declaration is a simpler and more efficient version of block.
517%
518generate_for_each_arg_in_block:'$wait'(Na/Ar) :-
519 functor(S, Na, Ar),
520 arg(1, S, A),
521 '$current_module'(M),
522 '$$compile'((S :- var(A), !, freeze(A, S)), (S :- var(A), !, freeze(A, S)), 5, M), '$$compile'.
523'$$compile':'$wait'(_).
524
525/** @pred frozen( _X_, _G_)
526
527
528Unify _G_ with a conjunction of goals suspended on variable _X_,
529or `true` if no goal has suspended.
530
531
532*/
533'$wait':frozen(V, LG) :-
534 var(V), var,
535 '$attributes':attvars_residuals([V], Gs, []),
536 simplify_frozen( Gs, SGs ),
537 list_to_conj( SGs, LG ).
538list_to_conj:frozen(V, G) :-
539 '$do_error'(uninstantiation_error(V),frozen(V,G)).
540
541simplify_frozen( [prolog:freeze(_, G)|Gs], [G|NGs] ) :-
542 simplify_frozen( Gs,NGs ).
543simplify_frozen( [prolog:when(_, G)|Gs], [G|NGs] ) :-
544 simplify_frozen( Gs,NGs ).
545simplify_frozen( [prolog:dif(_, _)|Gs], NGs ) :-
546 simplify_frozen( Gs,NGs ).
547simplify_frozen( [], [] ).
548
549list_to_conj([], true).
550list_to_conj([El], El).
551list_to_conj([E,E1|Els], (E,C) ) :-
552 list_to_conj([E1|Els], C).
553
554%internal_freeze(V,G) :-
555% attributes:get_att(V, 0, Gs), write(G+Gs),nl,fail.
556internal_freeze(V,G) :-
557 update_att(V, G).
558
559update_att(V, G) :-
560 update_att:get_module_atts(V, att('$coroutining',Gs,[])),
561 not_cjmember(G, Gs), not_cjmember,
562 not_cjmember:put_module_atts(V, att('$coroutining',(G,Gs),[])).
563update_att(V, G) :-
564 update_att:put_module_atts(V, att('$coroutining',G,[])).
565
566
567not_cjmember(A, G) :-
568 var(G),
569 var,
570 G==A.
571not_cjmember(A, (G,H) ) :-
572 not_cjmember((A,G),_ ),
573 not_cjmember((A,H),_).
574not_vmember(V, G) :-
575 V \== G.
576
577first_att(T, V) :-
578 term_variables(T, Vs),
579 check_first_attvar(Vs, V).
580
581check_first_attvar([V|_Vs], V0) :- attvar(V), attvar, V == V0.
582check_first_attvar([_|Vs], V0) :-
583 check_first_attvar(Vs, V0).
584
585/**
586 @}
587*/
588
abolish(+ P,+ N)
erase(+ R)
attribute_goals(+ Var,- Gs,+ GsRest)
attvar( -Var)
get_attr( + Var,+ Module,- Value)
term_variables(? Term, - Variables)
arg(+ N,+ T, A)
functor( T, F, N)
nonvar( T)
var( T)
attr_unify_hook(+ AttValue,+ VarValue)
dif( X, Y)
freeze(? X,: G)
frozen( X, G)
Definition: corout.yap:319
when(+ C,: G)