
On Fri, Feb 17, 2017 at 06:06:26PM -0500, Joseph Hackman wrote: [...]
I think it would be key, like async/await, to narrowly define the scope in which the word delayed functions as a keyword.
The PEP makes it clear that's just a transition phase: they will be turned into proper keywords in Python 3.7. https://www.python.org/dev/peps/pep-0492/#id80 Python has had "pseudo-keywords" in the past, like "as": [steve@ando ~]$ python2.5 -c "import math as as; print as" <module 'math' from '/usr/local/lib/python2.5/lib-dynload/math.so'> and it is my understanding that the core developers dislike this sort of thing. As do I. You shouldn't count as getting the same special treament as async/await. Maybe you will, maybe you won't.
A new keyword means it can't be back-ported to older versions, and will break code.
async and await both work fine, for the reasons listed above.
You're missing the point: code that uses async and await, whether as pseduo-keywords or actual keywords, cannot easily be backported to Python 3.4 or older. If Python introduces a new built-in, say Aardvark, then it can be back-ported: try: Aardvark except NameError: from backport import Aardvark No such thing is possible for new syntax. So that counts as a disadvantage of new syntax. Are we positive that there *must* be new syntax to solve this problem? (I think probably so, but it still counts as a disadvantage: that means that the usefulness is reduced.)
Unlike 'lambda' which returns a function (so the receiver must be lambda-aware), delayed execution blocks are for all purposes values. The first time the value (rather than location) is read,
What counts as "reading" a value? Based on your example below, I can't tell if passing the object to *any* function is enough to trigger evaluation, or specifically print is the magic that makes it happen.
So far I'm going with pretty much anything that isn't being the right-hand of an assignment. So coercion to different types, hashing (for use as a key in a dict or set), __repr__, etc would all be covered, as well as identity and comparisons. i.e.: [...]
That will make it pretty much impossible to tell whether something is a delayed "thunk" or not, since *any* attempt to inspect it in any way will cause it to reify. Maybe that's what we want.
That's easily done by having the "delayed" keyword cache each expression it sees, but that seems like a bad idea to me:
spam = delayed: get_random_string() eggs = delayed: get_random_string() # the same expression
spam.upper() # convert to a real value
assert spam == eggs # always true, as they are the same expression
Since spam and eggs are two different instances of delayed expression, each one would be evaluated separately when they are read from (as operands for the equals operator). So no, even without the spam.upper(), they would not match.
Earlier we talked about delayed *expressions* always generating the same value, now you're talking about *instances* rather than expressions. It makes sense to have keep the standard Python object semantics, rather than have the value of a delayed thunk cached by the textual expression that generated it.
I think it is better to stick to a more straight-forward, easily understood and debugged system based on object identity rather than expressions.
The caching means that: spam = delayed: calculate(1) eggs = spam
eggs == spam would be true, and calculate would have only been called once, not twice.
That's not caching, that's simple identity. That's how assignment works in Python, delayed calculation or not. -- Steve