Confessions of a Python fanboy

C

Chris Rebert

Cheers, but what about this:

 def goBig(x):
    while True:
        x = x ** 2
        yield x

for result in goBig(10):
    if result > 10 ** 100:
        break
    print result

It's a silly example, but wouldn't goBig(10) in this example be a "full
anonymous function"?

No, because it has a name, namely "goBig"; this obviously prevents it
from being "anonymous".

For comparison, note how the function in the following example is
never given a name, and is thus anonymous:11

Cheers,
Chris
 
J

Jean-Michel Pichavant

r said:
The purpose of his thread was to get feedback on how Python
and Ruby ideas could be cumulated into the best high level language.
And being that i am the BDFL of the "Confessions of a Python Fanboy"
thread, you have my personal permission to continue on with this
subject matter...,
Challenging even the most accepted mechanisms of a language is a proof
of intelligence.

Descartes's doubt method :
"The basic strategy of /Descartes/
<http://www.philosophypages.com/ph/desc.htm>'s method of doubt
<http://www.philosophypages.com/dy/d9.htm#doubt> is to defeat skepticism
<http://www.philosophypages.com/dy/s5.htm#skep> on its own ground. Begin
by doubting the truth of everything—not only the evidence
<http://www.philosophypages.com/dy/e9.htm#evid> of the senses and the
more extravagant cultural presuppositions, but even the fundamental
process of reasoning itself. If any particular truth about the world can
survive this extreme skeptical challenge, then it must be truly
indubitable and therefore a perfectly certain foundation for knowledge"

So let's make the method call parenthesis a "truly indubitable and
therefore a perfectly certain foundation".

Those who want to remove the parenthesis on python method calls raise
the hand !

JM
 
M

Masklinn

I believe "full" anonymous functions was intended by the author.
lambdas are limited to a single expression.
Yes, and they're limited to a single *expression*, so before Python 3,
lambda: print "foo" is out. Likewise, it isn't possible to have an if/
else statement within a lambda (though a ternary is ok), or a with, …
since Python statements aren't expressions.
 
I

Iain King

Oh my, "r" is still around is he??? And now he's singing the praises of
Ruby, the language which he treated as the Devil's Spawn when he first
arrived. That's hilarious.

But back on topic... "r" has missed the point. It's not that a=b is hard
to understand because b is a poor name. The example could have been:

def factory_function():
    magic = time.time()  # or whatever
    def inner():
        return magic
    return inner

my_function = factory_function

It's still ambiguous. Does the programmer intend my_function to become
factory_function itself, or the output of factory_function?

Not only that - does 'return inner' return the function inner or the
result of function inner?

How does ruby pass a function as an object?

Iain
 
M

Masklinn

Not only that - does 'return inner' return the function inner or the
result of function inner?

How does ruby pass a function as an object?
Ruby doesn't have functions as such. It has methods and blocks (which
would be anonymous functions). `def` always creates methods.

To get a (bound) method object, you simply call `method` on an
instance e.g. `foo.method:)bar)` is equivalent to `foo.bar` in Python
(it returns a bound method object without calling it).

Blocks are usually created as part of the calls: `foo.bar
{do_something}` the part between braces (braces included) is a block,
which will be passed to the method `bar`. Sadly, Ruby's blocks are
magical syntax (doesn't mean they have to be, in Smalltalk there's
nothing magical about blocks for instance) so you can't just do `foo =
{do_something}`, you have to turn them into `Proc` objects with the
`proc` constructor (or `lambda`, it's equivalent and looks better so
I'll use that): `foo = lambda {do_something}`. If you use the magical
syntax previously shown, Ruby handles the turning of a block into an
actual `Proc` instance.

And since Ruby doesn't have a `()` operator, it uses a method instead
(`#call`), so you simply do `foo.call` to execute the proc and get its
value.

All in all, much like Smalltalk, Ruby tends not to favor raw functions
the way Python does, so a direct translation of the Python code
doesn't make much sense.
 
B

Bruno Desthuilliers

Steven D'Aprano a écrit :
That's no different from Python's "constant by convention".

Well, at least Python doesn't pretend to have real symbolic constants -
we all know it's only a convention !-)
We don't even
get a compiler warning!

Of course - from the compiler's POV, it's only usual rebinding.
On the other hand, we don't have to prefix names with @ and @@,

Nope, we have to prefix them with 'self' or 'cls' (or even
'self.__class__').
and we
don't have the compiler trying to *guess* whether we're calling a
function or referring to a variable.

Please re-read a bit more carefully - it's *all* method call. Python is
'uniform' in obj.name is always an attribute lookup (methods being
attributes), Ruby is uniform in that 'obj.name' is always a method call.
Somebody who knows more Ruby than me should try writing the Zen of Ruby.
Something like:

(snip childish parody of Python Zen)

Steven, is that any useful ?
Although I'm sure Ruby has its good points. I'm not convinced anonymous
code blocks are one of them though.

Ruby's code blocks come from Smalltalk, where they are an absolute
necessity since message passing (which code blocks are part of) is the
*only* builtin control flow in Smalltalk - so you just *need* this
construction to provide branching and iterations.

Wether it makes sense to have code blocks in Ruby is another question
since Ruby does provide traditional control flow features, but then one
could question Python's "lambda" (hem...) too.
 
M

Masklinn

Steven D'Aprano a écrit :
Ruby's code blocks come from Smalltalk, where they are an absolute
necessity since message passing (which code blocks are part of) is
the *only* builtin control flow in Smalltalk - so you just *need*
this construction to provide branching and iterations.
I'm not so sure about the way you say it. I'm pretty sure
"traditional" flow control structures preceded Smalltalk by a pair of
decades so it's not that Smalltalk's designers found it necessary to
use blocks & messages, but that they understood blocks & messages
could trivially replace most control structures (making *those*
unnecessary), making the core language simpler and more flexible.

In other words, I see it the other way around.
Wether it makes sense to have code blocks in Ruby is another
question since Ruby does provide traditional control flow features
Well it does at least allow for the creation of new flow control
structures in library land when the existing ones aren't enough (e.g.
allows Ruby not to require the introduction of a `with` statement).
Though Ruby's blocks are nowhere near as flexible as Smalltalk's.
 
B

Bruno Desthuilliers

Masklinn a écrit :
I'm not so sure about the way you say it.

I'm not sure about the way you understand it !-)
I'm pretty sure "traditional"
flow control structures preceded Smalltalk by a pair of decades

Yes, of course - and that's not the point. What's important is that:

so it's
not that Smalltalk's designers found it necessary to use blocks &
messages, but that they understood blocks & messages could trivially
replace most control structures (making *those* unnecessary), making the
core language simpler and more flexible.
Exactly.

In other words, I see it the other way around.

My wording may have been a bit confusing, indeed. It was implied (but
perhaps a bit unclear) that restricting control structures to messages
and blocks was a design choice.
>
Well it does at least allow for the creation of new flow control
structures in library land when the existing ones aren't enough (e.g.
allows Ruby not to require the introduction of a `with` statement).

Yeps. But then other "traditionnal" control flow features become redundant.
 
M

Masklinn

Masklinn a écrit :
[misunderstandings on my part/clarifications on yours]
Yeps. But then other "traditionnal" control flow features become
redundant.
They can be anyway: Ruby doesn't deprecate most control flows as the
actual usages of blocks are a bit restricted (cannot be used for
`while` as it would require the serialization to a Proc, and Ruby's
syntax doesn't allow sending multiple blocks to a method so `if…else`
is out as well). And I assume they reintroduced the for…in sugar to
ease the transition from more traditional languages (using #each and
others seems the suggested collection iterators across the community).
 
E

Ethan Furman

Steven said:
That's no different from Python's "constant by convention". We don't even
get a compiler warning!

That's quite different, actually. Python doesn't claim to have
constants! Can't misinterpret what you can't have, can you?

[Holds breath while awaiting counter-example... :]

~Ethan~
 
I

Iain King

That's no different from Python's "constant by convention". We don't even
get a compiler warning!

That's quite different, actually.  Python doesn't claim to have
constants!  Can't misinterpret what you can't have, can you?

[Holds breath while awaiting counter-example... :]

~Ethan~

The convention being detailed in PEP8: http://www.python.org/dev/peps/pep-0008/
basically, anything in ALL_CAPS is a constant, assuming you follow
those style guidelines.

Iain
 
B

Benjamin Kaplan

That and the fact that I couldn't stop laughing for long enough to learn
any more when I read in the Pragmatic Programmer's Guide that "Ruby,
unlike less flexible languages, lets you alter the value of a constant.."
Yep, as they say "Bug" = "Undocumented feature"!
That's no different from Python's "constant by convention". We don't even
get a compiler warning!

That's quite different, actually.  Python doesn't claim to have
constants!  Can't misinterpret what you can't have, can you?

[Holds breath while awaiting counter-example... :]

~Ethan~

The convention being detailed in PEP8: http://www.python.org/dev/peps/pep-0008/
basically, anything in ALL_CAPS is a constant, assuming you follow
those style guidelines.

Right, but that's a style guide and not a language definition. Nobody
claims that anything in there is a part of the language.
 
S

Steven D'Aprano

Nope, we have to prefix them with 'self' or 'cls' (or even
'self.__class__').
Incorrect.
.... class_attribute = 'No @@ required.'
....'No @@ required.'


No 'self' or 'cls' in sight.


I can see a vague advantage to using @ to create instance attributes and
@@ to creating class attributes -- it makes it easy to create class
attributes inside a method without needing to do a double lookup. In
Python terms:

def K:
def method(self):
self.__class__.attr = "Class attribute"

would be:

def K:
def method(self):
@@attr = "Class attribute"

Advantages: you save a few characters and (possibly!) a couple of runtime
lookups.

Disadvantages: your code is filled with line noise. It's an arbitrary
choice between @@ meaning instance attribute and @@ meaning class
attribute -- there's no logical reason for choosing one over the other,
so you have to memorise which is which. It's easy to get it wrong.

'self' and 'cls' at least are words, even if 'cls' is badly misspelled :)


Please re-read a bit more carefully - it's *all* method call.

What did I misread from here?

When Ruby sees a name such as ``a'' in an expression, it needs to
determine if it is a local variable reference or a call to a method with
no parameters. To decide which is the case, Ruby uses a heuristic. As
Ruby reads a source file, it keeps track of symbols that have been
assigned to. It assumes that these symbols are variables. When it
subsequently comes across a symbol that might be either a variable or a
method call, it checks to see if it has seen a prior assignment to that
symbol. If so, it treats the symbol as a variable; otherwise it treats it
as a method call.
[end quote]

And see the example "pathological case" comparing a function call (not
method) and a variable.

http://ruby-doc.org/docs/ProgrammingRuby/html/language.html

Python is
'uniform' in obj.name is always an attribute lookup (methods being
attributes), Ruby is uniform in that 'obj.name' is always a method call.

Which would be relevant if I was talking about method calls, but I wasn't.


(snip childish parody of Python Zen)

Steven, is that any useful ?

It made me feel good.

But seriously, while I admit that I have very little Ruby experience, and
so aren't in a great position to judge, it seems to me that Ruby doesn't
have anything like Python's over-riding design principles (the Zen). If
there is a design principle to Ruby, I can't see what it is.

I'm the first to admit that I'm far too inexperienced with the language
to make this a fair judgement. Unfair it might be, but it doesn't
necessarily mean I'm wrong! *wink*

Ruby just seems to be far more complicated than Python: things which
Python does at runtime, with a function call, Ruby has special syntax for:

"a bunch of words".split()
%w(a bunch of words)

ord('a')
?a

That makes the barrier to entry far higher: it's easier to leverage
existing knowledge to interpret unfamiliar Python code than unfamiliar
Ruby code. Or so it seems to me.

Oh, and I admit that Python decorators are a conspicuous counter-example.
I wouldn't do without them, but neither would I expect somebody to intuit
what they do.

Ruby's code blocks come from Smalltalk, where they are an absolute
necessity since message passing (which code blocks are part of) is the
*only* builtin control flow in Smalltalk - so you just *need* this
construction to provide branching and iterations.

Just because Smalltalk had a particular (mis?)feature doesn't mean that
other languages should copy it. I know, I know, Ruby people swear by
anonymous code blocks, and I've read Paul Graham too. But I'm really not
so sure that the benefits of anonymous code blocks are great enough to
overcome the disadvantages of anonymous code blocks.

Wether it makes sense to have code blocks in Ruby is another question
since Ruby does provide traditional control flow features, but then one
could question Python's "lambda" (hem...) too.

lambda, by allowing the function to be only a single expression, doesn't
suffer the disadvantages of anonymous code blocks. lambda, by allowing
the function to be only a single expression, also has fewer advantages
than anonymous code blocks. I think lambda ends up on the "more
advantages than disadvantages" side. I'm keeping my mind open regarding
Ruby code blocks.
 
M

Masklinn

But seriously, while I admit that I have very little Ruby
experience, and
so aren't in a great position to judge, it seems to me that Ruby
doesn't
have anything like Python's over-riding design principles (the Zen).
If
there is a design principle to Ruby, I can't see what it is.
As far as I know, Ruby doesn't have anything like the Zen no. But then
again, I don't know any language other than Python with such a document.
Ruby just seems to be far more complicated than Python: things which
Python does at runtime, with a function call, Ruby has special
syntax for:

"a bunch of words".split()
%w(a bunch of words)

ord('a')
?a
That's the Perl heritage. And since it inherits from Perl, TIMTOWTDI:
>> "a bunch of words".split => ["a", "bunch", "of", "words"]
>> "a"[0]
=> 97
(yes, the indexing operator returns a character code if provided a
single index. With two, it returns as string slice)

Oh and %w() isn't really equivalent to split(): you don't use it to
split a string but to create a list of strings, so the equivalent
expression in Python would be `["a", "bunch", "of", "words"]`.
That makes the barrier to entry far higher: it's easier to leverage
existing knowledge to interpret unfamiliar Python code than unfamiliar
Ruby code. Or so it seems to me.
That's probable.
Just because Smalltalk had a particular (mis?)feature doesn't mean
that
other languages should copy it. I know, I know, Ruby people swear by
anonymous code blocks, and I've read Paul Graham too. But I'm really
not
so sure that the benefits of anonymous code blocks are great enough to
overcome the disadvantages of anonymous code blocks.
What are the disadvantages of anonymous functions?
 
T

Terry Reedy

Masklinn said:
#each is simply a method that takes a function (called blocks in ruby).
One could call it a higher-order method I guess.

It's an implementation of the concept of internal iteration: instead of
collections yielding iterator objects, and programmers using those
through specially-built iteration constructs (e.g. `for…in`),
collections control iteration over themselves (the iteration is
performed "inside" the collection, thus the "internal" part) and the
programmer provides the operations to perform at each iterative step
through (usually) a function.

Python's iterator protocol was developed in part to avoid the
(inside-out) callback style of programming. Writing virtual collections
as generator functions instead of iterator or iterable classes saves a
lot of boilerplate code. The itertools modules shows how nicely
iterators can be composed in a way that is much more awkward with callbacks.
In Python (assuming we had anonymous defs and an each method on lists),
the following loop:

for item in some_list:
do_something(item)
do_something_else(item)

some_list.each((def (item):
do_something(item)
do_something_else(item)
))

And how does Ruby do the equivalent of

def double(it):
for i in it:
yield 2*i

for i,j in zip(double(some_list), some_gen_func(args)):
print(do_something(i+j,i-j))

Terry Jan Reedy
 
F

Falcolas

I believe "full" anonymous functions was intended by the author.
lambdas are limited to a single expression.

Yes, and they're limited to a single *expression*, so before Python 3,  
lambda: print "foo" is out. Likewise, it isn't possible to have an if/
else statement within a lambda (though a ternary is ok), or a with, …  
since Python statements aren't expressions.[/QUOTE]

Perhaps you can't do lambda foo: print foo, but you *can* do lambda x:
sys.stdout.write(x).

Combined with the ternary if, it seem sufficient if you want to stick
to the ideal "Simple is better than Complex". Sure, with statements
allowed in anonymous functions, you can save on a line of typing (def
blargh(x):), but I don't feel the obfuscation is worth the cost.

~G
 
T

Tim Rowe

2009/7/31 Steven D'Aprano said:
That's no different from Python's "constant by convention". We don't even
get a compiler warning!

We don't actually *declare* that something is constant and then have
that declaration ignored. Python doesn't lie to us, although (as in
any language) a programmer might.
 
M

Masklinn

Python's iterator protocol was developed in part to avoid the
(inside-out) callback style of programming. Writing virtual
collections as generator functions instead of iterator or iterable
classes saves a lot of boilerplate code. The itertools modules shows
how nicely iterators can be composed in a way that is much more
awkward with callbacks.


And how does Ruby do the equivalent of

def double(it):
for i in it:
yield 2*i

for i,j in zip(double(some_list), some_gen_func(args)):
print(do_something(i+j,i-j))


Somethign along the lines of

some_list.map{|e| 2*e}.zip(some_gen_func(args)).each {|i, j|
puts(do_something(i+j, i-j))
}

The `#each` call after `#zip` is not actually necessary, but I find it
clearer. Oh, and some_gen_func and do_something probably wouldn't work
that way (as I said above, Ruby isn't big on named functions and
doesn't actually have them)
 
S

Steven D'Aprano

What are the disadvantages of anonymous functions?

In no particular order, and not necessarily exhaustive:

* The risk of obfuscation in your code. That's fairly minimal for
lambdas, because they're just a single expression, but for a large
anonymous code block (ACB) defined inside a named function, it may be
difficult for the reader to easily distinguish which bits are the outer
function and which are the ACB.


* Loss of useful debugging information. Take this example from Python:
.... return f(3)
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in main
.... return 2/(n-3)
....Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in main
File "<stdin>", line 2, in my_special_function
ZeroDivisionError: integer division or modulo by zero

If your code has only one anonymous function (whether a lambda or a full
multi-line block), then it's easy to identify which lambda raised the
exception: there is only one it could be. But if your code uses lots of
lambdas, the lack of a function name makes it hard to distinguish one
<lambda> from another <lambda>. Anonymity makes identification harder.


* Risk of code-duplication and breaking the principle of Once And Only
Once. Anonymous functions are generally created, used, then immediately
thrown away -- or at least made more-or-less inaccessible for reuse. An
anonymous function stored in a callback still exists, but the coder isn't
able to easily re-use it for another callback somewhere else in the code.
Consequently, there's a temptation for the coder to write the same
function multiple times:

add_button("Parrot", colour=blue, callback=lambda x: x.stuff('a'))
add_button("Cheese", flavour=tasty, callback=lambda x: x.thing('b'))
add_button("Canary", colour=yellow, callback=lambda x: x.stuff('a'))

instead of:

def bird_callback(x):
return x.stuff('a')

add_button("Parrot", colour=blue, callback=bird_callback)
add_button("Cheese", flavour=tasty, callback=lambda x: x.thing('b'))
add_button("Canary", colour=yellow, callback=bird_callback)

* Recursion is more or less impossible without fragile tricks.

(At least for Python. I don't know how recursion operates in Ruby.)
 
E

Emmanuel Surleau

We don't actually *declare* that something is constant and then have
that declaration ignored. Python doesn't lie to us, although (as in
any language) a programmer might.

You could say that Ruby doesn't either, you just need to read the
documentation. Ruby's unwritten motto is "flexibility über alles". In this
regard, it is consistent (1). Not much is really bolted down in Ruby. You get
encapsulation, but it's so easy to break that it's mostly symbolic. It's a
language which gives great power to the programmer. Whether Ruby programmers
handle this power responsibly is another debate.

Cheers,

Emm

(1) I find Ruby to be pretty consistent in general, which is not always the
case of Python.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top