Light-weight call-by-name syntax in Python

Proposal: Light-weight call-by-name syntax in Python The following syntax a : b is to be interpreted as: a(lambda: b) Effectively, this gives a "light-weight macro system" to Python, since it allows with little syntax to indicate that the argument to a function is not to be immediately invoked. It is a generalization of special-case syntax proposals like delayed: <expr> In this proposal, `delayed' can be a normal callable. Motivating examples follow: # Logging # The following assumes the logging library has been extended to support # a callable argument in addition to a string. import logging logging.debug: "I have a %s" % expensive_function() # Spawn parallel tasks # This would work with the existing concurrent.futures unmodified. from concurrent.futures import ThreadPoolExecutor executor = ThreadPoolExecutor(max_workers=4) future = executor.submit: some_long_running_function(another_long_one()) # Asyncio loop.call_soon_threadsafe: do_something(42) # Custom asserts from some_module_implementing_contracts import precondition def foo(x): precondition: x >= 0 return math.sqrt(x) # Lazy evaluation class delayed: def __init__(self, callback): self.callback = callback self.value = None def __call__(self): if self.callback is not None: self.value = self.callback() self.callback = None return self.value promise = delayed: a() + b() print(promise()) # default dictionary dd = collections.defaultdict: MyDefaultObject(42) print(dd["foo"]) # => prints MyDefaultObject(42) Notes on syntax: a : b is my preferred syntax. It would conflict with existing use of : in a few places, e.g. in if and while. My preferred approach is that we would need to write (a : b) there. An alternative is a as-yet-unused token such as :: . Stephan

On Fri, Feb 17, 2017 at 2:22 AM, Stephan Houben <stephanh42@gmail.com> wrote:
Note that this is definitely a different proposal from the original, since the original proposer's goal was to be able to use this with existing, unmodified functions that expect a regular value, not a lambda. I don't really see how that goal can be accomplished without massively revising Python's runtime model, so this doesn't really bother me, but it should be noted :-). Anyway, you might also be interested in: https://mail.python.org/pipermail/python-ideas/2016-November/043590.html which is a similar idea and some more motivating examples, except that it allows for the full richness of Python's call syntax, and passes ASTs rather than lambdas to allow for non-standard evaluation rules. -n -- Nathaniel J. Smith -- https://vorpus.org

Hi Nathaniel, 2017-02-17 11:28 GMT+01:00 Nathaniel Smith <njs@pobox.com>:
Yep ;-)
Frankly, I think that proposal is too baroque. The simplicity of my proposal is intentional. Just passing in a callable makes it simpler and makes it also more likely that existing functions could be used with it. I have considered adding all kind of syntax for adding extra arguments to the wrapped callable, extra arguments for the invoked function, ... basically that just makes it a mess. If you want that, use the existing syntax. By the way, got a private e-mail mentioning that the proposed syntax is already claimed by type annotations. So therefore a :: b is now my proposed syntax. Stephan

On Fri, Feb 17, 2017 at 2:46 AM, Stephan Houben <stephanh42@gmail.com> wrote:
I feel like using Python's existing call syntax is less baroque then inventing a new, redundant call syntax that doesn't look like anything else in the language, but I suppose tastes will differ :-).
Just passing in a callable makes it simpler and makes it also more likely that existing functions could be used with it.
This is also potentially a downside, though... the macro-call proposal not only handles the cases you're worrying about, but also handles a bunch of cases that are basically impossible to handle in Python right now. OTOH all the cases where your proposal is useful are ones that Python can already handle at the cost of adding 6 characters. That's not a terribly compelling argument for violating TOOWTDI, IMO. -n -- Nathaniel J. Smith -- https://vorpus.org

On Fri, 17 Feb 2017 at 10:23 Stephan Houben <stephanh42@gmail.com> wrote:
This is, in my view, one case where Python's existing lambda syntax is perfectly sufficient. (I'd even argue it might be the *only* case...) If you could logger.debug(lambda: expensive_to_compute_message_here), I don't think that the delayed-expression proposal would ever have existed. Ed

On Fri, Feb 17, 2017 at 2:22 AM, Stephan Houben <stephanh42@gmail.com> wrote:
Note that this is definitely a different proposal from the original, since the original proposer's goal was to be able to use this with existing, unmodified functions that expect a regular value, not a lambda. I don't really see how that goal can be accomplished without massively revising Python's runtime model, so this doesn't really bother me, but it should be noted :-). Anyway, you might also be interested in: https://mail.python.org/pipermail/python-ideas/2016-November/043590.html which is a similar idea and some more motivating examples, except that it allows for the full richness of Python's call syntax, and passes ASTs rather than lambdas to allow for non-standard evaluation rules. -n -- Nathaniel J. Smith -- https://vorpus.org

Hi Nathaniel, 2017-02-17 11:28 GMT+01:00 Nathaniel Smith <njs@pobox.com>:
Yep ;-)
Frankly, I think that proposal is too baroque. The simplicity of my proposal is intentional. Just passing in a callable makes it simpler and makes it also more likely that existing functions could be used with it. I have considered adding all kind of syntax for adding extra arguments to the wrapped callable, extra arguments for the invoked function, ... basically that just makes it a mess. If you want that, use the existing syntax. By the way, got a private e-mail mentioning that the proposed syntax is already claimed by type annotations. So therefore a :: b is now my proposed syntax. Stephan

On Fri, Feb 17, 2017 at 2:46 AM, Stephan Houben <stephanh42@gmail.com> wrote:
I feel like using Python's existing call syntax is less baroque then inventing a new, redundant call syntax that doesn't look like anything else in the language, but I suppose tastes will differ :-).
Just passing in a callable makes it simpler and makes it also more likely that existing functions could be used with it.
This is also potentially a downside, though... the macro-call proposal not only handles the cases you're worrying about, but also handles a bunch of cases that are basically impossible to handle in Python right now. OTOH all the cases where your proposal is useful are ones that Python can already handle at the cost of adding 6 characters. That's not a terribly compelling argument for violating TOOWTDI, IMO. -n -- Nathaniel J. Smith -- https://vorpus.org

On Fri, 17 Feb 2017 at 10:23 Stephan Houben <stephanh42@gmail.com> wrote:
This is, in my view, one case where Python's existing lambda syntax is perfectly sufficient. (I'd even argue it might be the *only* case...) If you could logger.debug(lambda: expensive_to_compute_message_here), I don't think that the delayed-expression proposal would ever have existed. Ed
participants (4)
-
Ed Kellett
-
Erik
-
Nathaniel Smith
-
Stephan Houben