
Silly Python riddle - cool-RR
http://blog.garlicsim.org/post/14506657016/silly-python-riddle
======
ul5255
>>> f = lambda: g((yield))

>>> f()

<generator object <lambda> at 0x00B50828>

~~~
molbioguy
Sorry for the dumb question (I'm still inexperienced with python), but can
someone explain why the yield needs to be enclosed in ()?

~~~
obtu
yield was first introduced as a statement similar to return. In Python not
every statement is an expression; another example of non-expression statement
is assignment, which was banned from expressions so that it couldn't be
confused with ==.

Since then someone proposed (PEP 342) that yield could be used not just to
yield values from coroutines, but to send one back in. yield becomes an
expression, mostly used as the right side of an assignment. The parentheses
are there because the grammar seems to handle the yield statement and yield
expressions differently (the PEP mentions that "yield 12, 42" would have
become confusing).

~~~
wycats
Stuff like this makes me seriously doubt whether Python critiques of Ruby's
"blocks and methods" (SO MANY CALLABLES; ONE AND ONLY ONE) are actually made
in good faith.

~~~
saurik
I might misunderstand the point you are making, but this actually stems from a
"one and only one" mentality: there is only a single entry for "yield" in the
grammar, and it is defined to take a specific kind of expression as its
argument.

(edit:) (Additionally, there is the "one and only one" mentality that if yield
is valid in a expression for a normal method, it should be valid in an
expression for a lambda as well; however, that doesn't cause this syntax
issue: statement vs. expression does.)

This level of expression is allowed to contain a comma. As an example, you can
do "yield 1, 2", which will yield a 2-tuple containing 1 and 2 as elements.

However, if you wish to embed it as an argument in a function call, you will
end up with an ambiguity, as arguments to functions are comma separated. You
therefore must embed this expression in parentheses, lest you get a weird
grammar conflict.

To be clear, this is identical to the case with tuples themselves, with the
one caveat that they decided to make it actually be a syntax error to use it
in a way that causes the ambiguity (I will be clear that I do not know if I
agree with their decision on that regard).

"return 1, 2" <\- valid

"a, b = 1, 2" <\- valid

"a((1, 2))" <\- valid

"a(1, 2)" <\- not what you meant

(edit:) JavaScript has a similar problem, by the way: they have all sorts of
special variants of expressions that are not allowed to start with a brace or
the keyword "function" due to ambiguities at the top levels of statements.

However, the most similar issue in JavaScript is with relation to the keyword
"in": "for (var x = 5 in a" could either mean "loop over x, initialized to 5,
as all keys of a" or "initialize x as whether 5 is a key in a".

You can figure out the difference once you get to the next token: ")" (the
former) or ";" (the latter), but once you get that far you've already
committed to specific parse tree formulations for the code you've already
seen.

~~~
wycats
There are two kinds of callables. Functions, which can contain multiple
expressions, and lambdas, which can contain only one.

~~~
saurik
Ah, ok; that didn't seem related to this specific discussion of yield (yield
is valuable to have as an expression even in a multi-statement function), but
I can see how it is reminiscent.

It should be noted, though, that when you use the lambda keyword, the result
is a function, and has the same properties and behaviors as a function
(including that it can contain yield and be a generator).

The lambda syntax is simply a way to declare a function as an expression. You
can't have a multi-statement block embedded in an expression due to
fundamental limitations of normative whitespace.

There really, though, is only one kind of "callable": "function". There are
two syntaxes for declaring it, however: "def", which takes a block of
statements, and "lambda", which takes an expression.

This is (to me) drastically different than the behavior difference that I
believe is what you are referring to in Ruby, where Proc and lambda have
different semantics for embedded usages of the "return" keyword.

(That said, I want to be clear, to both you and any random audience: I
personally agree with Ruby's decision to have these two forms of callable, per
real-world conversations I've had with wycats.)

(afterword) Someone else might find this interesting: I was going to also make
the point that both lambda and def result in the same class (function), but it
turns out that Proc.new and lambda do as well (Proc).

------
martin_k
I'm not sure, why you think the value of the lambda function is thrown away.
The lambda function returns a generator object. The function body of a
generator is generally not executed until you call .next() on it, so that's
why you don't get a NameError instantly. Also the value, that you .send() into
the generator is not thrown away either. In your example the generator just
runs into an exception before it goes into a state in which it would accept a
.send() call. Just consider this modified example:

    
    
      >>> f=lambda: (yield)
      >>> gen=f()
      >>> gen.next()
      >>> gen.send('foobar')
      'foobar'

~~~
cool-RR
I think you misunderstood which value gets thrown away.

Consider this:

    
    
        >>> g = lambda x: 7
        >>> f = lambda: g((yield))
        >>> list(f())
        [None]
    

Can you tell me where the 7 went to? As far as I see, it went into oblivion.

~~~
martin_k
As i said, f() returns a generator object in this case. So you're calling list
on a generator that yields None once, the result of which is a list that
contains None. If you change the `yield` to `yield "foobar"`, list(f()) will
get you `["foobar"]`.

Edit: Perhaps, to clear things up more, about where the value 7 went- your
snippet can be expressed without using lambdas as

    
    
      >>> def g(x):
      ...  return 7
      ... 
      >>> def f():
      ...  v = (yield)
      ...  g(v)
      ... 
      >>> list(f())
      [None]
    

Note that there's no return before g(v) because generators (at least in 2.7)
can only yield, not return a value.

~~~
pyre
I'm thinking that the parent is expecting 'yield' to return 'None' into g(),
and then for g() to return 7 since it doesn't do anything with the 'x'
parameter, and is confused why that isn't the case.

~~~
martin_k
Yes, but I think this is cleared up if you consider that calling a generator
function does not execute its body, but return generator object. It doesn't
really matter if it's a lambda or a regular named function.

------
phzbOx
I had this (with 8 characters):

    
    
      );g=(int
    

giving:

    
    
      f = lambda: g();g=(int)
      f() -> 0

~~~
phzbOx
There is also this: f = lambda: g();g=(f)

Which produce a 'RuntimeError: maximum recursion depth exceeded'; But that's
only because my computer is not powerful enough :)

~~~
rplnt
And it's even shorter than the _yield_ one.

------
nyellin
I haven't been following the thread, but Ram posted the same riddle on Python-
IL mailing list a few days ago. You can see the discussion there.

[http://hamakor.org.il/pipermail/python-
il/2011-December/thre...](http://hamakor.org.il/pipermail/python-
il/2011-December/thread.html)

------
teaspoon

      """

~~~
cool-RR
Doesn't count... If only for the fact that you won't get a >>> prompt after
it.

------
ngkabra
At 12 characters, this is definitely not the shortest, but it is a totally
different approach than the other solutions:

    
    
        >>> f = lambda: g()if 0 else(1)
        >>> f()
        1

~~~
jerfelix
Creative solution! You can knock it down a character by removing the "1" and
simply returning an empty tuple.

    
    
        >>> f = lambda: g()if 0 else()
        >>> f()
        ()

------
sapphirecat
I was going to attempt this, but the solution was posted inline in the blog
entry, and I read it by accident. My window was _just_ big enough that it was
the last line displayed. =(

~~~
cool-RR
Sorry, I couldn't find a way to blacken the text with Tumblr.

I now did a (Scroll down...) thing to "hide" the solution.

------
jgeralnik
Something like

);f=(set

Leading to the whole line reading:

f = lambda: g();f=(set)

8 characters

~~~
aroberge
Alternatively

);f=set#

------
VMG
the winning solutions seems to be 7 chars

------
makeramen
0);g=(id

8 chars

------
coppero1237
f = lambda g:(0) f() <function <lambda> at 0x113478>.

------
jmathes
f = lambda: g(""")

f()

technically not two commands, and it will look like:

>>> f = lambda: g(""")

. . . f()

