44This is essentially a toy that has no more power than list comprehensions
55or nested for loops. An important feature of McCarthy's amb operator is its
66nonlocality - being able to jump back to a choice point, even after the
7- dynamic extent of the function where it resides. (Sounds a lot like call/cc;
8- which is how amb is usually implemented in Scheme.)
7+ dynamic extent of the function where it resides. (Sounds a lot like
8+ ``call/cc``; which is how `` amb`` is usually implemented in Scheme.)
99
1010Instead, what we have here is essentially a tuple comprehension that:
1111
1717The implementation is based on the List monad. This is a hack with the bare
1818minimum of components to make it work, complete with a semi-usable syntax.
1919
20- For a friendlier syntax, if you use MacroPy, see ``unpythonic.syntax.forall``.
20+ If you use MacroPy:
2121
22- If you need more monads, look into OSlash .
22+ - For a friendlier syntax for this, see ``unpythonic.syntax.forall`` .
2323
24- Or if you want to roll your own, the parts of this module come from:
24+ - If you need the full(-ish) power of ``call/cc``, see
25+ ``unpythonic.syntax.continuations`` (which can implement ``amb``).
26+
27+ If you need more monads, look into the ``OSlash`` library.
28+
29+ If you want to roll your own monads, the parts for this module come from:
2530 https://github.com/Technologicat/python-3-scicomp-intro/blob/master/examples/monads.py
2631"""
2732
@@ -47,7 +52,9 @@ def choice(**binding):
4752 for k , v in binding .items (): # just one but we don't know its name
4853 return Assignment (k , v )
4954
50- # For a cleaner solution, see unpythonic.syntax.forall.
55+ # Hacky code generator, because Python has ``eval`` but no syntactic macros.
56+ # For a cleaner solution based on AST transformation with MacroPy,
57+ # see unpythonic.syntax.forall.
5158def forall (* lines ):
5259 """Nondeterministically evaluate lines.
5360
@@ -76,25 +83,26 @@ def forall(*lines):
7683 - All choices are evaluated, depth first, and set of results is
7784 returned as a tuple.
7885
79- - The last line describes one item of the return value.
80-
8186 - If a line returns an iterable, it is implicitly converted into a List
8287 monad containing the same items.
8388
8489 - This applies also to the RHS of a ``choice``.
8590
86- - If a line returns a single item, it is wrapped into a singleton List.
87-
88- - The final result is **not** unpacked. This allows easily returning
89- a tuple from the computation.
91+ - As the only exception, the last line describes one item of the return
92+ value; there the implicit conversion is skipped.
9093
91- - The final result is converted from List monad to tuple for output.
94+ This allows easily returning a tuple (as one result item) from the
95+ computation, as in the above pythagorean triples example.
9296
93- - The variables currently picked by the choices live in the environment.
94- To access it, use a ``lambda e: ...`` like in ``unpythonic.letrec`` .
97+ - If a line returns a single item, it is wrapped into a singleton List
98+ (a List containing that one item) .
9599
96- - The List monad is internal to this module.
100+ - The final result (containing all the results) is converted from
101+ List monad to tuple for output.
97102
103+ - The values currently picked by the choices are bound to names in
104+ the environment. To access it, use a ``lambda e: ...`` like in
105+ ``unpythonic.letrec``.
98106
99107 Quick vocabulary for haskellers:
100108 - ``forall(...)`` = ``do ...``
@@ -185,7 +193,8 @@ def begin(*exprs): # args eagerly evaluated by Python
185193
186194# print(allcode) # DEBUG
187195
188- # The eval'd code doesn't close over the current lexical scope,
196+ # The eval'd code doesn't close over the current lexical scope at the site
197+ # of the eval call, but runs in its own initially blank environment,
189198 # so provide the necessary names as its globals.
190199 mlst = eval (allcode , {"e" : e , "bodys" : bodys , "begin" : begin , "monadify" : monadify })
191200 return tuple (mlst )
0 commit comments