Skip to content

Commit bdff3c2

Browse files
committed
Rename setescape/escape to catch/throw
Also, make the escape continuation analysis in `unpythonic.syntax.util` respect also `throw` as the invocation of an escape continuation. Resolves #46.
1 parent d9ddb4b commit bdff3c2

File tree

8 files changed

+144
-89
lines changed

8 files changed

+144
-89
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,16 @@ If you still use 3.4 and find something in `unpythonic` doesn't work there, plea
3333
- `box` now supports `.set(newvalue)` to rebind (returns the new value as a convenience), and `unbox(b)` to extract contents. Syntactic sugar for rebinding is `b << newvalue` (where `b` is a box).
3434
- `islice` now supports negative start and stop. (**Caution**: no negative step; and it must consume the whole iterable to determine where it ends, if at all.)
3535

36+
**Non-breaking changes**:
37+
38+
- `setscape`/`escape` have been renamed `catch`/`throw`, to match the standard terminology in the Lisp family. **The old nonstandard names are now deprecated, and will be removed in 0.15.0.**
39+
3640
**Fixed**:
3741

3842
- Fix initialization crash in `lazyutil` if MacroPy is not installed.
3943
- Fix bug in `identity` and `const` with zero args (#7).
4044
- Use standard Python semantics for negative indices (#6).
45+
- Escape continuation analysis in `unpythonic.syntax.util` now interprets also the literal name `throw` as invoking an escape continuation.
4146

4247
---
4348

doc/features.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ The exception are the features marked **[M]**, which are primarily intended as a
4040
- [``trampolined``, ``jump``: tail call optimization (TCO) / explicit continuations](#trampolined-jump-tail-call-optimization-tco--explicit-continuations)
4141
- [``looped``, ``looped_over``: loops in FP style (with TCO)](#looped-looped_over-loops-in-fp-style-with-tco)
4242
- [``gtrampolined``: generators with TCO](#gtrampolined-generators-with-tco): tail-chaining; like ``itertools.chain``, but from inside a generator.
43-
- [``setescape``, ``escape``: escape continuations (ec)](#setescape-escape-escape-continuations-ec)
43+
- [``catch``, ``throw``: escape continuations (ec)](#catch-throw-escape-continuations-ec)
4444
- [``call_ec``: first-class escape continuations](#call_ec-first-class-escape-continuations), like Racket's ``call/ec``.
4545
- [``forall``: nondeterministic evaluation](#forall-nondeterministic-evaluation), a tuple comprehension with multiple body expressions.
4646

@@ -2173,57 +2173,55 @@ last(take(10000, fibos())) # no crash
21732173
```
21742174
21752175
2176-
### ``setescape``, ``escape``: escape continuations (ec)
2177-
2178-
This feature is known as `catch`/`throw` in several Lisps, e.g. in Emacs Lisp and in Common Lisp (as well as some of its ancestors). This terminology is independent of the use of `throw`/`catch` in C++/Java for the exception handling mechanism. Common Lisp also provides a lexically scoped variant (`BLOCK`/`RETURN-FROM`) that is more idiomatic [according to Seibel](http://www.gigamonkeys.com/book/the-special-operators.html).
2176+
### ``catch``, ``throw``: escape continuations (ec)
21792177
21802178
Escape continuations can be used as a *multi-return*:
21812179
21822180
```python
2183-
from unpythonic import setescape, escape
2181+
from unpythonic import catch, throw
21842182
2185-
@setescape() # note the parentheses
2183+
@catch() # note the parentheses
21862184
def f():
21872185
def g():
2188-
escape("hello from g") # the argument becomes the return value of f()
2186+
throw("hello from g") # the argument becomes the return value of f()
21892187
print("not reached")
21902188
g()
21912189
print("not reached either")
21922190
assert f() == "hello from g"
21932191
```
21942192
2195-
**CAUTION**: The implementation is based on exceptions, so catch-all ``except:`` statements will intercept also escapes, breaking the escape mechanism. As you already know, be specific in what you catch!
2193+
**CAUTION**: The implementation is based on exceptions, so catch-all ``except:`` statements will intercept also throws, breaking the escape mechanism. As you already know, be specific in which exception types you catch in an `except` clause!
21962194
2197-
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 `escape(value)`. The function decorated with `@setescape` immediately terminates, returning ``value``.
2195+
In Lisp terms, `@catch` essentially captures the escape continuation (ec) of the function decorated with it. The nearest (dynamically) surrounding ec can then be invoked by `throw(value)`. When the `throw` is performed, the function decorated with `@catch` immediately terminates, returning ``value``.
21982196
21992197
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. It is a function call, so it works also in lambdas.
22002198
22012199
Escaping the function surrounding an FP loop, from inside the loop:
22022200
22032201
```python
2204-
@setescape()
2202+
@catch()
22052203
def f():
22062204
@looped
22072205
def s(loop, acc=0, i=0):
22082206
if i > 5:
2209-
escape(acc)
2207+
throw(acc)
22102208
return loop(acc + i, i + 1)
22112209
print("never reached")
22122210
f() # --> 15
22132211
```
22142212
2215-
For more control, both ``@setescape`` points and escape instances can be tagged:
2213+
For more control, both ``@catch`` points and ``throw`` instances can be tagged:
22162214
22172215
```python
2218-
@setescape(tags="foo") # setescape point tags can be single value or tuple (tuples OR'd, like isinstance())
2216+
@catch(tags="foo") # catch point tags can be single value or tuple (tuples OR'd, like isinstance())
22192217
def foo():
22202218
@call
2221-
@setescape(tags="bar")
2219+
@catch(tags="bar")
22222220
def bar():
22232221
@looped
22242222
def s(loop, acc=0, i=0):
22252223
if i > 5:
2226-
escape(acc, tag="foo") # escape instance tag must be a single value
2224+
throw(acc, tag="foo") # throw instance tag must be a single value
22272225
return loop(acc + i, i + 1)
22282226
print("never reached")
22292227
return False
@@ -2232,20 +2230,24 @@ def foo():
22322230
assert foo() == 15
22332231
```
22342232
2235-
For details on tagging, especially how untagged and tagged escapes and points interact, and how to make one-to-one connections, see the docstring for ``@setescape``.
2233+
For details on tagging, especially how untagged and tagged throw and catch points interact, and how to make one-to-one connections, see the docstring for ``@catch``.
2234+
2235+
**Etymology**
2236+
2237+
This feature is known as `catch`/`throw` in several Lisps, e.g. in Emacs Lisp and in Common Lisp (as well as some of its ancestors). This terminology is independent of the use of `throw`/`catch` in C++/Java for the exception handling mechanism. Common Lisp also provides a lexically scoped variant (`BLOCK`/`RETURN-FROM`) that is more idiomatic [according to Seibel](http://www.gigamonkeys.com/book/the-special-operators.html).
22362238
22372239
22382240
#### ``call_ec``: first-class escape continuations
22392241
2240-
We provide ``call/ec`` (a.k.a. ``call-with-escape-continuation``), in Python spelled as ``call_ec``. It's a decorator that, like ``@call``, 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 a **first-class escape continuation** to the callee.
2242+
We provide ``call/ec`` (a.k.a. ``call-with-escape-continuation``), in Python spelled as ``call_ec``. It's a decorator that, like ``@call``, immediately runs the function and replaces the def'd name with the return value. The twist is that it internally sets up an catch point, and hands a **first-class escape continuation** to the callee.
22412243
22422244
The function to be decorated **must** take one positional argument, the ec instance.
22432245
2244-
The ec instance itself is another function, which takes one positional argument: the value to send to the escape point. The ec instance and the escape point are connected one-to-one. No other ``@setescape`` point will catch the ec instance, and the escape point catches only this particular ec instance and nothing else.
2246+
The ec instance itself is another function, which takes one positional argument: the value to send to the catch point. The ec instance and the catch point are connected one-to-one. No other ``@catch`` point will catch the ec instance, and the catch point catches only this particular ec instance and nothing else.
22452247
22462248
Any particular ec instance is only valid inside the dynamic extent of the ``call_ec`` invocation that created it. Attempting to call the ec later raises ``RuntimeError``.
22472249
2248-
This builds on ``@setescape`` and ``escape``, so the caution about catch-all ``except:`` statements applies here, too.
2250+
This builds on ``@catch`` and ``throw``, so the caution about catch-all ``except:`` statements applies here, too.
22492251
22502252
```python
22512253
from unpythonic import call_ec
@@ -2427,7 +2429,7 @@ def result():
24272429
print(result) # (6, 7)
24282430
```
24292431
2430-
(But see ``@setescape``, ``escape``, and ``call_ec``.)
2432+
(But see ``@catch``, ``throw``, and ``call_ec``.)
24312433
24322434
Compare the sweet-exp Racket:
24332435

macro_extras/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -914,9 +914,9 @@ The ``tco`` and ``continuations`` macros actually share a lot of the code that i
914914

915915
It is important to recognize a call to an escape continuation as such, because the argument given to an escape continuation is essentially a return value. If this argument is itself a call, it needs the TCO transformation to be applied to it.
916916

917-
For escape continuations in ``tco`` and ``continuations`` blocks, only basic uses of ``call_ec`` are supported, for automatically harvesting names referring to an escape continuation. In addition, the literal function names ``ec`` and ``brk`` are always *understood as referring to* an escape continuation.
917+
For escape continuations in ``tco`` and ``continuations`` blocks, only basic uses of ``call_ec`` are supported, for automatically harvesting names referring to an escape continuation. In addition, the literal function names ``ec``, ``brk`` and ``throw`` are always *understood as referring to* an escape continuation.
918918

919-
The name ``ec`` or ``brk`` alone is not sufficient to make a function into an escape continuation, even though ``tco`` (and ``continuations``) will think of it as such. The function also needs to actually implement some kind of an escape mechanism. An easy way to get an escape continuation, where this has already been done for you, is to use ``call_ec``.
919+
The name ``ec``, ``brk`` or ``throw`` alone is not sufficient to make a function into an escape continuation, even though ``tco`` (and ``continuations``) will think of it as such. The function also needs to actually implement some kind of an escape mechanism. An easy way to get an escape continuation, where this has already been done for you, is to use ``call_ec``. Another such mechanism is the ``catch``/``throw`` pair.
920920

921921
See the docstring of ``unpythonic.syntax.tco`` for details.
922922

0 commit comments

Comments
 (0)