forked from Technologicat/unpythonic
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_conts_escape.py
More file actions
116 lines (108 loc) · 5.45 KB
/
Copy pathtest_conts_escape.py
File metadata and controls
116 lines (108 loc) · 5.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# -*- coding: utf-8 -*-
"""Using continuations as an escape mechanism."""
from ...syntax import macros, test # noqa: F401
from ...test.fixtures import session, testset
from ...syntax import macros, tco, continuations, call_cc # noqa: F401, F811
from ...ec import call_ec
def runtests():
# basic strategy using an escape continuation
with testset("basic ec"):
def double_odd(x, ec):
if x % 2 == 0: # reject even "x"
ec("not odd")
return 2 * x
@call_ec
def result1(ec):
y = double_odd(42, ec) # noqa: F841, this is just a silly example for testing.
z = double_odd(21, ec) # pragma: no cover, should not be reached.
return z # pragma: no cover, should not be reached.
@call_ec
def result2(ec):
y = double_odd(21, ec) # noqa: F841, this is just a silly example for testing.
z = double_odd(42, ec)
return z # pragma: no cover, should not be reached.
test[result1 == "not odd"]
test[result2 == "not odd"]
# should work also in a "with tco" block
with testset("basic ec in a TCO block"):
with tco:
def double_odd(x, ec): # noqa: F811, the previous one is no longer used.
if x % 2 == 0: # reject even "x"
ec("not odd")
return 2 * x
@call_ec
def result1(ec):
y = double_odd(42, ec) # noqa: F841, this is just a silly example for testing.
z = double_odd(21, ec) # avoid tail-calling because ec is not valid after result1() exits # pragma: no cover, should not be reached.
return z # pragma: no cover, should not be reached.
@call_ec
def result2(ec):
y = double_odd(21, ec) # noqa: F841, this is just a silly example for testing.
z = double_odd(42, ec)
return z # pragma: no cover, should not be reached.
test[result1 == "not odd"]
test[result2 == "not odd"]
# can we do this using the **continuations** machinery?
with testset("continuations idea"):
with continuations:
def double_odd(x, ec, cc): # noqa: F811, the previous one is no longer used.
if x % 2 == 0:
cc = ec # try to escape by overriding cc... # noqa: F841
return "not odd"
return 2 * x
def main1(cc):
# cc actually has a default, so it's ok to not pass anything as cc here.
y = double_odd(42, ec=cc) # y = "not odd" # noqa: F841, this is just a silly example for testing.
z = double_odd(21, ec=cc) # we could tail-call, but let's keep this similar to the first example.
return z
def main2(cc):
y = double_odd(21, ec=cc) # noqa: F841, this is just a silly example for testing.
z = double_odd(42, ec=cc)
return z
# ...but no call_cc[] anywhere, so cc is actually always
# unpythonic.fun.identity, cannot perform an escape.
test[main1() == 42]
test[main2() == "not odd"]
# to fix that, let's call_cc[]:
with testset("continuations implementation"):
with continuations:
def double_odd(x, ec, cc): # noqa: F811, the previous one is no longer used.
if x % 2 == 0:
cc = ec # escape by overriding cc (now it works!) # noqa: F841
return "not odd"
return 2 * x
def main1(cc):
y = call_cc[double_odd(42, ec=cc)] # noqa: F841, this is just a silly example for testing.
z = call_cc[double_odd(21, ec=cc)] # pragma: no cover, should not be reached.
return z # pragma: no cover, should not be reached.
def main2(cc):
y = call_cc[double_odd(21, ec=cc)] # noqa: F841, this is just a silly example for testing.
z = call_cc[double_odd(42, ec=cc)]
return z # pragma: no cover, should not be reached.
# call_cc[] captures the actual cont, so now this works as expected.
test[main1() == "not odd"]
test[main2() == "not odd"]
# In each case, the second call_cc[] is actually redundant, because after
# the second call to double_odd(), there is no more code to run in each
# main function.
#
# We can just as well use a tail-call, optimizing away a redundant
# continuation capture:
with testset("small optimization"):
with continuations:
def double_odd(x, ec, cc): # noqa: F811, the previous one is no longer used
if x % 2 == 0:
cc = ec # noqa: F841
return "not odd"
return 2 * x
def main1(cc):
y = call_cc[double_odd(42, ec=cc)] # noqa: F841, this is just a silly example for testing.
return double_odd(21, ec=cc) # tail call, no further code to run in main1 so no call_cc needed. # pragma: no cover, should not be reached.
def main2(cc):
y = call_cc[double_odd(21, ec=cc)] # noqa: F841, this is just a silly example for testing.
return double_odd(42, ec=cc)
test[main1() == "not odd"]
test[main2() == "not odd"]
if __name__ == '__main__': # pragma: no cover
with session(__file__):
runtests()