I’d say it’s more about properties. Their __name__ is <lambda>, you can’t pickle them, etc. I’m not sure if you can re-write lambdas to functions and bring those properties back. I guess this is a case about semantics, but I think that these properties are important to keep, since there are programs that do care about it, and the author’s post was about any Python program in general.
The OP doesn’t have async or comprehensions. It’s much harder to fake those. All you have to do for the lambda thing, if you really think it’s important that they have a meaningless name, is rewrite a few properties and make a replacement code object (which is immutable and annoyingly also has a name). Child’s play!
Async is actually fairly easy. asycnio existed even before the async/await syntax was introduced. Async functions (in 3.8, before it was different) return an object with an __await__ method that returns an iterator, that the async runtime(usually asyncio, but others exist) iterate through, and get the requests for IO from, and sends back the results to the object via a send() method. await exists for connecting the iterators together conveniently, though it was (and still is) possible to do so via yield from. The parts in the language were there for a long time already, it just took a while to put it together and make it comfortable.
Comprehensions can usually be trivially rewritten into generators. A simple case like (<item> for <identifiers> in <source> [if <condition>]) can be rewritten into
def gen():
for <identifiers> in <source>:
if <condition>:
yield <item>
It of course gets a bit more complicated when you get into multiple for’s in a comprehension, but that is still not too complicated to do.
As for lambdas, your implementation might be workable, but that’s too much details for me to look into right now.
Yes, it’s easy to get the important behaviour of each, but for lambdas you were asking for something that’s indistinguishable. I believe my solution for lambdas yields something that is indistinguishable from a lambda constructed the usual way; I think it would be harder to do this with async or comprehensions.
I don’t think it is too difficult to make async to work in a way that is indistinguishable from the way it works right now. As for comprehensions, the objects are just standard generator objects, nothing fancy there to make it difficult.
I think you can write any Python with lambdas without lambdas by repeatedly replacing a top-level lambda (i.e. one that isn’t nested inside another lambda) with a named function. The trick is to be sure to name other expressions and intermediate results you encounter along the way, so that you don’t reorder the evaluation of the lambda definitions you’re rewriting with respect to anything else; example below.
I think this is hard for a human to do systematically without errors, but a compiler could do this sort of thing in its sleep, and a human could probably do this for all reasonable code, because reasonable code doesn’t tend to rely on effectful lambda definitions.
I love it. But how are classes transformed into this?
For that matter how are dictionaries and lists represented?
Edit: ok looking into it the class description as functions is described here and the dictionary representation as a class (object) is described here. More cumbersome than I was hoping.
I’m surprised to see that
:=
cannot be rewritten in other statements. Isn’ta := ...
equivalent totmp = ...; a = tmp
?:=
can be used in lambda’s, wheras=
cannot, and as you cannot re-implement lambdas in Python, you’ll need:=
to achieve full feature set.Why can’t lambdas be reimplemented as a simple “return foo” function defined right before the lambda usage? Something about different scope?
I’d say it’s more about properties. Their
__name__
is<lambda>
, you can’t pickle them, etc. I’m not sure if you can re-write lambdas to functions and bring those properties back. I guess this is a case about semantics, but I think that these properties are important to keep, since there are programs that do care about it, and the author’s post was about any Python program in general.The OP doesn’t have
async
or comprehensions. It’s much harder to fake those. All you have to do for the lambda thing, if you really think it’s important that they have a meaningless name, is rewrite a few properties and make a replacement code object (which is immutable and annoyingly also has a name). Child’s play!In MVPy:
edit: I guess if I’m using Python 3.8’s / syntax I can use CodeType.replace too
Async is actually fairly easy.
asycnio
existed even before theasync
/await
syntax was introduced. Async functions (in 3.8, before it was different) return an object with an__await__
method that returns an iterator, that the async runtime(usuallyasyncio
, but others exist) iterate through, and get the requests for IO from, and sends back the results to the object via asend()
method.await
exists for connecting the iterators together conveniently, though it was (and still is) possible to do so viayield from
. The parts in the language were there for a long time already, it just took a while to put it together and make it comfortable.Comprehensions can usually be trivially rewritten into generators. A simple case like
(<item> for <identifiers> in <source> [if <condition>])
can be rewritten intoIt of course gets a bit more complicated when you get into multiple
for
’s in a comprehension, but that is still not too complicated to do.As for lambdas, your implementation might be workable, but that’s too much details for me to look into right now.
Yes, it’s easy to get the important behaviour of each, but for lambdas you were asking for something that’s indistinguishable. I believe my solution for lambdas yields something that is indistinguishable from a lambda constructed the usual way; I think it would be harder to do this with
async
or comprehensions.I don’t think it is too difficult to make
async
to work in a way that is indistinguishable from the way it works right now. As for comprehensions, the objects are just standard generator objects, nothing fancy there to make it difficult.I think you can write any Python with lambdas without lambdas by repeatedly replacing a top-level lambda (i.e. one that isn’t nested inside another lambda) with a named function. The trick is to be sure to name other expressions and intermediate results you encounter along the way, so that you don’t reorder the evaluation of the lambda definitions you’re rewriting with respect to anything else; example below.
I think this is hard for a human to do systematically without errors, but a compiler could do this sort of thing in its sleep, and a human could probably do this for all reasonable code, because reasonable code doesn’t tend to rely on effectful lambda definitions.
I love it. But how are classes transformed into this?
For that matter how are dictionaries and lists represented?
Edit: ok looking into it the class description as functions is described here and the dictionary representation as a class (object) is described here. More cumbersome than I was hoping.