You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+64-1Lines changed: 64 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -553,7 +553,9 @@ def f():
553
553
f() # --> 15
554
554
```
555
555
556
-
In Lisp terms, `@setescape` essentially captures the escape continuation (ec) of the function decorated with it. The nearest (dynamically) surrounding ec can then be invoked by `raise escape(value)`. The escaped function immediately terminates, returning ``value``.
556
+
**CAUTION**: Because the implementation is based on exceptions, catch-all``except:`` statements will intercept also ``escape`` instances, breaking the escape mechanism. As you already know, be specific in what you catch!
557
+
558
+
In Lisp terms, `@setescape` essentially captures the escape continuation (ec) of the function decorated with it. The nearest (dynamically) surrounding ec can then be invoked by `raise escape(value)`. The escaped function immediately terminates, returning ``value``. In Python terms, an escape means just raising a specific type of exception; the usual rules concerning ``try``/``except``/``else``/``finally``and``with`` blocks apply.
557
559
558
560
To make this work with lambdas, andfor uniformity of syntax, **in trampolined functions** (such asFP loops) it is also legal to ``return escape(value)``. The trampoline specifically detects `escape` instances, and performs the ``raise``.
559
561
@@ -594,6 +596,67 @@ def f():
594
596
assert f() =="hello from g"
595
597
```
596
598
599
+
#### First-class escape continuations (call/ec)
600
+
601
+
We provide also ``call/ec`` (``call-with-escape-continuation``), in Python spelled as``call_ec``. It's a decorator that, like ``@immediate``, immediately runs the function and replaces the def'd name with the return value. The twist is that it internally sets up an escape point, and hands over a **first-class escape continuation** to the callee.
602
+
603
+
The function to be decorated **must** take one positional argument, the ec instance.
604
+
605
+
The ec itself is another function, which takes one positional argument: the value to send to the escape point. Both the ec instance and the escape point are tagged with a temporary process-wide unique id that connects them. (Untagged ``@setescape`` points may still catch this escape; this may be subject to change in a later version.)
606
+
607
+
A particular ec instance is only valid inside the dynamic extent of the ``call_ec`` invocation that created it. Attempting to call it later raises ``RuntimeError``.
608
+
609
+
The above caution about catch-all``except:`` statements applies here, too.
610
+
611
+
```python
612
+
@call_ec
613
+
def result(ec): # effectively, just a code block, capturing the ec as an argument
614
+
answer=42
615
+
ec(answer) # here this has the same effect as "return answer"...
616
+
print("never reached")
617
+
answer=23
618
+
return answer
619
+
assert result ==42
620
+
621
+
@call_ec
622
+
def result(ec):
623
+
answer=42
624
+
def inner():
625
+
ec(answer) # ...but here this directly escapes from the outer def
626
+
print("never reached")
627
+
return23
628
+
answer= inner()
629
+
print("never reached either")
630
+
return answer
631
+
assert result ==42
632
+
```
633
+
634
+
This also works with lambdas, by using ``call_ec()`` directly:
635
+
636
+
```python
637
+
result= call_ec(lambdaec:
638
+
begin(print("hi from lambda"),
639
+
ec(42),
640
+
print("never reached")))
641
+
assert result ==42
642
+
```
643
+
644
+
Normally ``begin()`` would return the last value, but the ec overrides that; it is effectively a ``return``for multi-expression lambdas!
645
+
646
+
And named functions, why not those too:
647
+
648
+
```python
649
+
def f(ec):
650
+
print("hi from f")
651
+
ec(42)
652
+
print("never reached")
653
+
654
+
# ...possibly somewhere else, possibly much later...
655
+
656
+
result= call_ec(f)
657
+
assert result ==42
658
+
```
659
+
597
660
### Dynamic scoping
598
661
599
662
A bit like global variables, but slightly better-behaved. Useful for sending some configuration parameters through several layers of function calls without changing their API. Best used sparingly. Similar to [Racket](http://racket-lang.org/)'s [`parameterize`](https://docs.racket-lang.org/guide/parameterize.html).
0 commit comments