Skip to content

Commit 6e90e4d

Browse files
committed
consistency: allow only rebinding in set() also in let, lispylet
1 parent 5f1d6f5 commit 6e90e4d

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

unpythonic/let.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,12 @@ def __str__(self):
9191
def set(self, name, value):
9292
"""Convenience method to allow assignment in expression contexts.
9393
94-
Like Scheme's set! function.
94+
Like Scheme's set! function. Only rebinding is allowed.
9595
9696
For convenience, returns the `value` argument.
9797
"""
98+
if not hasattr(self, name): # allow only rebinding
99+
raise AttributeError("name '{:s}' is not defined".format(name))
98100
setattr(self, name, value)
99101
return value # for convenience
100102

@@ -426,6 +428,14 @@ def result(*, env):
426428
return env.evenp(23)
427429
assert result is False
428430

431+
try:
432+
let(x=0,
433+
body=lambda e: e.set('y', 3)) # error, y is not defined
434+
except AttributeError as err:
435+
pass
436+
else:
437+
assert False
438+
429439
print("All tests PASSED")
430440

431441
if __name__ == '__main__':

unpythonic/lispylet.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def bletrec(bindings):
146146
This chains ``@dletrec`` and ``@immediate``."""
147147
return _blet(bindings, mode="letrec")
148148

149+
# TODO: switch to the same env implementation as let.py to avoid the k=set issue?
149150
class _env:
150151
"""Environment.
151152
@@ -154,11 +155,15 @@ class _env:
154155
def set(self, k, v):
155156
"""Convenience method to allow assignment in expression contexts.
156157
157-
For extra convenience, return the assigned value.
158+
Like Scheme's set! function. Only rebinding is allowed.
159+
160+
For convenience, returns the `value` argument.
158161
159162
DANGER:
160163
avoid the name ``k="set"``; it will happily shadow this method,
161164
because instance attributes are seen before class attributes."""
165+
if not hasattr(self, k): # allow only rebinding
166+
raise AttributeError("name '{:s}' is not defined".format(k))
162167
setattr(self, k, v)
163168
return v
164169

@@ -272,6 +277,14 @@ def result(*, env):
272277
return env.evenp(42)
273278
assert result is True
274279

280+
try:
281+
let((('x', 0),),
282+
lambda e: e.set('y', 3)) # error, y is not defined
283+
except AttributeError:
284+
pass
285+
else:
286+
assert False
287+
275288
print("All tests passed")
276289

277290
if __name__ == '__main__':

0 commit comments

Comments
 (0)