@@ -186,98 +186,16 @@ def baz():
186186 over fn.py's is the more natural syntax for the client code.
187187"""
188188
189- __all__ = ["SELF" , "jump" , "trampolined" , "looped" , "looped_over" , "escape" , "setescape" ]
189+ __all__ = ["SELF" , "jump" , "trampolined" , "looped" , "looped_over" ]
190190
191191from functools import wraps
192192
193193from unpythonic .misc import immediate
194+ from unpythonic .ec import escape
194195
195196# evil inspect dependency, used only to provide informative error messages.
196197from unpythonic .arity import arity_includes , UnknownArity
197198
198- # mainly for use with the looping constructs
199- class escape (Exception ):
200- """Exception that essentially represents an escape continuation.
201-
202- Use ``raise escape(value)`` to escape to the nearest (dynamically) surrounding
203- ``@setescape``. The @setescape'd function immediately terminates, returning
204- ``value``.
205-
206- Trampolined functions may also use ``return escape(value)``; the trampoline
207- will then raise the exception (this is to make it work also with lambdas).
208-
209- The optional ``tag`` parameter can be used to limit which ``@setescape``
210- points see this particular escape instance. Default is to be catchable
211- by any ``@setescape``.
212- """
213- def __init__ (self , value , tag = None ):
214- self .value = value
215- self .tag = tag
216-
217- # mainly for use with the looping constructs
218- def setescape (tag = None ):
219- """Decorator. Mark function as exitable by ``raise escape(value)``.
220-
221- In Lisp terms, this essentially captures the escape continuation (ec)
222- of the decorated function. The ec can then be invoked by raising escape(value).
223-
224- To make this work with lambdas, in trampolined functions it is also legal
225- to ``return escape(value)``. The trampoline specifically detects ``escape``
226- instances, and performs the ``raise``.
227-
228- Technically, this is a decorator factory; the optional tag parameter can be
229- used to catch only those escapes with the same tag. ``tag`` can be a single
230- value, or a tuple. Single value means catch that specific tag; tuple means
231- catch any of those tags. Default is None, i.e. catch all.
232-
233- Example::
234-
235- @setescape() # no tag, catch any escape instance
236- def f():
237- @looped
238- def s(loop, acc=0, i=0):
239- if i > 5:
240- return escape(acc) # the argument becomes the return value of f()
241- return loop(acc + i, i + 1)
242- print("never reached")
243- assert f() == 15
244-
245- Tagged escape::
246-
247- @setescape("foo")
248- def foo():
249- @immediate
250- @setescape("bar")
251- def bar():
252- @looped
253- def s(loop, acc=0, i=0):
254- if i > 5:
255- return escape(acc, tag="foo")
256- return loop(acc + i, i + 1)
257- print("never reached")
258- print("never reached either")
259- assert f() == 15
260- """
261- if tag is None :
262- tags = None
263- elif isinstance (tag , (tuple , list )): # multiple tags
264- tags = set (tag )
265- else : # single tag
266- tags = set ((tag ,))
267-
268- def decorator (f ):
269- @wraps (f )
270- def decorated (* args , ** kwargs ):
271- try :
272- return f (* args , ** kwargs )
273- except escape as e :
274- if tags is None or e .tag is None or e .tag in tags :
275- return e .value
276- else : # meant for someone else, pass it on
277- raise
278- return decorated
279- return decorator
280-
281199@immediate # immediate a class to make a singleton
282200class SELF : # sentinel, could be any object but we want a nice __repr__.
283201 def __repr__ (self ):
@@ -668,25 +586,17 @@ def baz():
668586 e .evenp (10000 ))
669587 assert t is True
670588
671- # "multi-return" using escape continuation
672- @setescape ()
673- def f ():
674- def g ():
675- raise escape ("hello from g" ) # the argument becomes the return value of f()
676- print ("not reached" )
677- g ()
678- print ("not reached either" )
679- assert f () == "hello from g"
680-
681589 # escape surrounding function from inside FP loop
590+ from unpythonic .ec import setescape
682591 @setescape ()
683592 def f ():
684593 @looped
685594 def s (loop , acc = 0 , i = 0 ):
686595 if i > 5 :
687596 return escape (acc ) # trampolined functions may also "return escape(...)"
688597 return loop (acc + i , i + 1 )
689- print ("never reached" )
598+ print ("not reached" )
599+ return False
690600 assert f () == 15
691601
692602 # setescape point tag can be single value or tuple (tuples OR'd, like isinstance())
0 commit comments