Skip to content

Commit 5d7936d

Browse files
committed
Rename mark_lazy -> passthrough_lazy_args
Also improve its explanation in docstrings and comments.
1 parent 562d920 commit 5d7936d

File tree

8 files changed

+79
-47
lines changed

8 files changed

+79
-47
lines changed

unpythonic/env.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
__all__ = ["env"]
55

66
from collections.abc import Container, Sized, Iterable, Mapping, MutableMapping
7-
from .lazyutil import mark_lazy
7+
from .lazyutil import passthrough_lazy_args
88

99
# co-operate with unpythonic.syntax.lazify; this is essentially a binding construct,
1010
# so it shouldn't force any promises that are stuffed into the env.
11-
@mark_lazy
11+
@passthrough_lazy_args
1212
class env:
1313
"""Environment for let-like constructs.
1414

unpythonic/fun.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
from .dynassign import dyn, make_dynvar
2626
from .regutil import register_decorator
2727

28-
# we use @mark_lazy (and handle possible lazy args) to support unpythonic.syntax.lazify.
29-
from .lazyutil import mark_lazy, islazy, force, force1, maybe_force_args
28+
# we use @passthrough_lazy_args (and handle possible lazy args) to support unpythonic.syntax.lazify.
29+
from .lazyutil import passthrough_lazy_args, islazy, force, force1, maybe_force_args
3030

3131
@register_decorator(priority=10)
3232
def memoize(f):
@@ -57,7 +57,7 @@ def memoized(*args, **kwargs):
5757
raise value
5858
return value
5959
if islazy(f):
60-
memoized = mark_lazy(memoized)
60+
memoized = passthrough_lazy_args(memoized)
6161
return memoized
6262

6363
#def memoize_simple(f): # essential idea, without exception handling
@@ -71,7 +71,7 @@ def memoized(*args, **kwargs):
7171
# return memoized
7272

7373
make_dynvar(curry_context=[])
74-
@mark_lazy
74+
@passthrough_lazy_args
7575
def _currycall(f, *args, **kwargs):
7676
"""Co-operate with unpythonic.syntax.curry.
7777
@@ -86,7 +86,7 @@ def _currycall(f, *args, **kwargs):
8686
return curry(f, *args, _curry_force_call=True, _curry_allow_uninspectable=True, **kwargs)
8787

8888
@register_decorator(priority=90)
89-
@mark_lazy
89+
@passthrough_lazy_args
9090
def curry(f, *args, _curry_force_call=False, _curry_allow_uninspectable=False, **kwargs):
9191
"""Decorator: curry the function f.
9292
@@ -200,7 +200,7 @@ def curried(*args, **kwargs):
200200
if len(args) < min_arity:
201201
p = partial(f, *args, **kwargs)
202202
if islazy(f):
203-
p = mark_lazy(p)
203+
p = passthrough_lazy_args(p)
204204
return curry(p)
205205
# passthrough on right, like https://github.com/Technologicat/spicy
206206
if len(args) > max_arity:
@@ -223,7 +223,7 @@ def curried(*args, **kwargs):
223223
return (now_result,) + later_args
224224
return maybe_force_args(f, *args, **kwargs)
225225
if islazy(f):
226-
curried = mark_lazy(curried)
226+
curried = passthrough_lazy_args(curried)
227227
curried._is_curried_function = True # stash for detection
228228
# curry itself is curried: if we get args, they're the first step
229229
if args or kwargs or _curry_force_call:
@@ -249,7 +249,7 @@ def flip(f):
249249
def flipped(*args, **kwargs):
250250
return maybe_force_args(f, *reversed(args), **kwargs)
251251
if islazy(f):
252-
flipped = mark_lazy(flipped)
252+
flipped = passthrough_lazy_args(flipped)
253253
return flipped
254254

255255
def rotate(k):
@@ -279,11 +279,11 @@ def rotated(*args, **kwargs):
279279
rargs = args[-j:] + args[:-j]
280280
return maybe_force_args(f, *rargs, **kwargs)
281281
if islazy(f):
282-
rotated = mark_lazy(rotated)
282+
rotated = passthrough_lazy_args(rotated)
283283
return rotated
284284
return rotate_k
285285

286-
@mark_lazy
286+
@passthrough_lazy_args
287287
def apply(f, arg0, *more, **kwargs):
288288
"""Scheme/Racket-like apply.
289289
@@ -366,7 +366,7 @@ def notf(f): # Racket: negate
366366
def negated(*args, **kwargs):
367367
return not maybe_force_args(f, *args, **kwargs)
368368
if islazy(f):
369-
negated = mark_lazy(negated)
369+
negated = passthrough_lazy_args(negated)
370370
return negated
371371

372372
def andf(*fs): # Racket: conjoin
@@ -392,7 +392,7 @@ def conjoined(*args, **kwargs):
392392
return False
393393
return b
394394
if all(islazy(f) for f in fs):
395-
conjoined = mark_lazy(conjoined)
395+
conjoined = passthrough_lazy_args(conjoined)
396396
return conjoined
397397

398398
def orf(*fs): # Racket: disjoin
@@ -420,7 +420,7 @@ def disjoined(*args, **kwargs):
420420
return b
421421
return False
422422
if all(islazy(f) for f in fs):
423-
disjoined = mark_lazy(disjoined)
423+
disjoined = passthrough_lazy_args(disjoined)
424424
return disjoined
425425

426426
def _make_compose1(direction): # "left", "right"
@@ -443,7 +443,7 @@ def compose1(fs):
443443
# - if fs contains only one item, we output it as-is
444444
composed = reducel(compose1_two, fs) # op(elt, acc)
445445
if all(islazy(f) for f in fs):
446-
composed = mark_lazy(composed)
446+
composed = passthrough_lazy_args(composed)
447447
return composed
448448
return compose1
449449

@@ -505,7 +505,7 @@ def composed(*args):
505505
def compose(fs):
506506
composed = reducel(compose_two, fs) # op(elt, acc)
507507
if all(islazy(f) for f in fs):
508-
composed = mark_lazy(composed)
508+
composed = passthrough_lazy_args(composed)
509509
return composed
510510
return compose
511511

@@ -591,7 +591,7 @@ def apply_f_to_kth_arg(*args):
591591
out.extend(args[m:])
592592
return tuple(out)
593593
if islazy(f):
594-
apply_f_to_kth_arg = mark_lazy(apply_f_to_kth_arg)
594+
apply_f_to_kth_arg = passthrough_lazy_args(apply_f_to_kth_arg)
595595
return apply_f_to_kth_arg
596596

597597
def to1st(f):
@@ -663,5 +663,5 @@ def fwithself(*args, **kwargs):
663663
#return f(fwithself, *args, **kwargs)
664664
return maybe_force_args(f, fwithself, *args, **kwargs) # support unpythonic.syntax.lazify
665665
if islazy(f):
666-
fwithself = mark_lazy(fwithself)
666+
fwithself = passthrough_lazy_args(fwithself)
667667
return fwithself

unpythonic/lazyutil.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
upon which other regular code is allowed to depend.
66
"""
77

8-
__all__ = ["mark_lazy", "maybe_force_args", "force1", "force"]
8+
__all__ = ["passthrough_lazy_args", "maybe_force_args", "force1", "force"]
99

1010
from .regutil import register_decorator
1111
from .dynassign import make_dynvar
@@ -31,19 +31,48 @@ class Lazy:
3131
# -----------------------------------------------------------------------------
3232

3333
@register_decorator(priority=95)
34-
def mark_lazy(f):
35-
"""Internal. Helps calling lazy functions from outside a ``with lazify`` block."""
36-
f._lazy = True
34+
def passthrough_lazy_args(f):
35+
"""Mark a function for passthrough of lazy args.
36+
37+
When a function has this mark, its arguments won't be forced by
38+
``maybe_force_args``. This is the only effect the mark has.
39+
40+
This is useful for decorating "infrastructure" functions that are strict
41+
(i.e. not lazy; defined outside any `with lazify` block), but do not need
42+
to access the values of all of their arguments. Usually the reason is that
43+
those arguments are just passed through for actual access elsewhere.
44+
45+
If needed, it's still possible to force individual arguments inside the
46+
body of a function decorated with this, using `force` or `force1`.
47+
48+
For example, `curry` uses this strategy to find out which function it
49+
should call (by forcing only its argument `f`), but it does not need to
50+
access the values of any of its other arguments. Those other arguments are
51+
just passed on to the function in question, so that's the correct place to
52+
make the lazy/strict distinction for those arguments. (`curry` then uses
53+
`maybe_force_args` to make the actual call, so that the call target is
54+
also checked for this mark.)
55+
56+
***CAUTION***: The mark is implemented as an attribute on the function
57+
object. Hence, if the result is wrapped by another decorator, the mark
58+
won't be active on the final decorated function.
59+
60+
The exact position where you want this in the decorator list depends
61+
on what exactly you're doing - the priority is set to `95` to make this
62+
apply before `curry`, so that `curry` will see the mark.
63+
"""
64+
f._passthrough_lazy_args = True
3765
return f
3866

3967
def islazy(f):
40-
"""Internal. Return whether the function f is marked as lazy.
68+
"""Return whether the function f is marked for passthrough of lazy args.
4169
42-
When a function is marked as lazy, its arguments won't be forced by
43-
``maybe_force_args``. This is the only effect the mark has.
70+
This is mainly used internally by `unpythonic.syntax.lazify`, but is
71+
provided as part of the public API, so that also user code can inspect
72+
the mark if it needs to.
4473
"""
4574
# special-case "_let" for lazify/curry combo when let[] expressions are present
46-
return hasattr(f, "_lazy") or (hasattr(f, "__name__") and f.__name__ == "_let")
75+
return hasattr(f, "_passthrough_lazy_args") or (hasattr(f, "__name__") and f.__name__ == "_let")
4776

4877
def maybe_force_args(f, *thunks, **kwthunks):
4978
"""Internal. Helps calling strict functions from inside a ``with lazify`` block."""

unpythonic/misc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
from queue import Empty
1515

1616
from .regutil import register_decorator
17-
from .lazyutil import mark_lazy, maybe_force_args, force
17+
from .lazyutil import passthrough_lazy_args, maybe_force_args, force
1818

1919
# Only the single-argument form (just f) is supported by unpythonic.syntax.util.sort_lambda_decorators.
2020
#
2121
# This is as it should be; if given any arguments beside f, the call doesn't conform
2222
# to the decorator API, but is a normal function call. See "callwith" if you need to
2323
# pass arguments and then call f from a decorator position.
2424
@register_decorator(priority=80)
25-
@mark_lazy
25+
@passthrough_lazy_args
2626
def call(f, *args, **kwargs):
2727
"""Call the function f.
2828
@@ -93,7 +93,7 @@ def _():
9393
return maybe_force_args(force(f), *args, **kwargs) # support unpythonic.syntax.lazify
9494

9595
@register_decorator(priority=80)
96-
@mark_lazy
96+
@passthrough_lazy_args
9797
def callwith(*args, **kwargs):
9898
"""Freeze arguments, choose function later.
9999

unpythonic/syntax/autoref.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .letdoutil import isdo, islet, ExpandedDoView, ExpandedLetView
1313

1414
from ..dynassign import dyn
15-
from ..lazyutil import force1, mark_lazy
15+
from ..lazyutil import force1, passthrough_lazy_args
1616

1717
# with autoref(o):
1818
# with autoref(scipy.loadmat("mydata.mat")): # evaluate once, assign to a gensym
@@ -69,7 +69,7 @@
6969
# to ``o`` and ``p`` directly), so that arbitrary expressions can be autoref'd without giving them
7070
# a name in user code.
7171

72-
@mark_lazy
72+
@passthrough_lazy_args
7373
def _autoref_resolve(args):
7474
*objs, s = [force1(x) for x in args]
7575
for o in objs:

unpythonic/syntax/lazify.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from .util import suggest_decorator_index, sort_lambda_decorators, detect_lambda, \
1616
isx, make_isxpred, getname, is_decorator, wrapwith
1717
from .letdoutil import islet, isdo, ExpandedLetView
18-
from ..lazyutil import mark_lazy, force, force1, maybe_force_args
18+
from ..lazyutil import passthrough_lazy_args, force, force1, maybe_force_args
1919
from ..dynassign import dyn
2020

2121
# -----------------------------------------------------------------------------
@@ -214,18 +214,18 @@ def f(tree):
214214
# to allow also strict code to call this function
215215
if type(tree) is Lambda:
216216
lam = tree
217-
tree = hq[mark_lazy(ast_literal[tree])]
217+
tree = hq[passthrough_lazy_args(ast_literal[tree])]
218218
tree = sort_lambda_decorators(tree)
219219
lam.body = rec(lam.body)
220220
else:
221221
tree.decorator_list = rec(tree.decorator_list)
222-
k = suggest_decorator_index("mark_lazy", tree.decorator_list)
222+
k = suggest_decorator_index("passthrough_lazy_args", tree.decorator_list)
223223
if k is not None:
224-
tree.decorator_list.insert(k, hq[mark_lazy])
224+
tree.decorator_list.insert(k, hq[passthrough_lazy_args])
225225
else:
226-
# mark_lazy should generally be as innermost as possible
226+
# passthrough_lazy_args should generally be as innermost as possible
227227
# (so that e.g. the curry decorator will see the function as lazy)
228-
tree.decorator_list.append(hq[mark_lazy])
228+
tree.decorator_list.append(hq[passthrough_lazy_args])
229229
tree.body = rec(tree.body)
230230

231231
elif type(tree) is Call:

unpythonic/syntax/tailtools.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from ..it import uniqify
3232
from ..fun import identity, orf
3333
from ..tco import trampolined, jump
34-
from ..lazyutil import mark_lazy
34+
from ..lazyutil import passthrough_lazy_args
3535

3636
# -----------------------------------------------------------------------------
3737
# Implicit return. This performs a tail-position analysis of function bodies.
@@ -134,7 +134,7 @@ def chain_conts(cc1, cc2, with_star=False): # cc1=_pcc, cc2=cc
134134
"""Internal function, used in code generated by the continuations macro."""
135135
if with_star: # to be chainable from a tail call, accept a multiple-values arglist
136136
if cc1 is not None:
137-
@mark_lazy
137+
@passthrough_lazy_args
138138
def cc(*value):
139139
return jump(cc1, cc=cc2, *value)
140140
else:
@@ -145,14 +145,14 @@ def cc(*value):
145145
cc = cc2
146146
else: # for inert data value returns (this produces the multiple-values arglist)
147147
if cc1 is not None:
148-
@mark_lazy
148+
@passthrough_lazy_args
149149
def cc(value):
150150
if isinstance(value, tuple):
151151
return jump(cc1, cc=cc2, *value)
152152
else:
153153
return jump(cc1, value, cc=cc2)
154154
else:
155-
@mark_lazy
155+
@passthrough_lazy_args
156156
def cc(value):
157157
if isinstance(value, tuple):
158158
return jump(cc2, *value)

unpythonic/tco.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ def baz():
131131
from sys import stderr
132132

133133
from .regutil import register_decorator
134-
from .lazyutil import islazy, mark_lazy, maybe_force_args
134+
from .lazyutil import islazy, passthrough_lazy_args, maybe_force_args
135135
from .dynassign import dyn
136136

137-
# In principle, jump should have @mark_lazy, but for performance reasons
137+
# In principle, jump should have @passthrough_lazy_args, but for performance reasons
138138
# it doesn't. "force(target)" is slow, so strict code shouldn't have to do that.
139139
# This is handled by a special case in maybe_force_args.
140140
def jump(target, *args, **kwargs):
@@ -284,7 +284,7 @@ def trampoline(*args, **kwargs):
284284
f = function
285285
while True:
286286
if callable(f): # the maybe_force_args here causes the performance hit
287-
v = maybe_force_args(f, *args, **kwargs)
287+
v = maybe_force_args(f, *args, **kwargs) # <--
288288
else:
289289
v = f
290290
if isinstance(v, _jump):
@@ -298,8 +298,11 @@ def trampoline(*args, **kwargs):
298298
return v
299299
if callable(function):
300300
trampoline._entrypoint = function
301-
if islazy(function): # <--
302-
trampoline = mark_lazy(trampoline) # <--
301+
# Mark the trampolined function for passthrough of lazy args if the
302+
# original function has the mark. This is needed because the mark is
303+
# implemented as an attribute on the function object.
304+
if islazy(function): # <--
305+
trampoline = passthrough_lazy_args(trampoline) # <--
303306
return trampoline
304307
else:
305308
return trampoline()

0 commit comments

Comments
 (0)