Interesting talk on Python vs. Ruby and how he would like Python to have just a bit more syntactic flexibility.

Steve Howell showell30 at yahoo.com
Thu Feb 18 11:15:46 EST 2010


On Feb 18, 7:50 am, Duncan Booth <duncan.bo... at invalid.invalid> wrote:
> Steve Howell <showel... at yahoo.com> wrote:
> > If this is an argument against using anonymous functions, then it is a
> > quadruple strawman.
>
> > Shipping buggy code is a bad idea, even with named functions.
>
> I doubt very much whether I have ever shipped any bug-free code but
> even if it was fit for purpose when shipped it is quite possible that the
> software will interact badly with other software that did not exist at the
> time of shipping.
>
>
>
> > Obscuring line numbers is a bad idea, even with named functions.
>
> In principle I agree, but where Javascript is concerned compressing the
> downloaded files is generally a pretty good idea and practicality beats
> purity.
>
>
>
> > Having your customers stay on older versions of your software is a bad
> > idea, even with named functions.
>
> I think that's their decision, not mine.
>
>
>
> > Not being able to know which version of software you're customer is
> > running is a bad idea, even with named functions.
> mpr
> I agree, but getting a complete coherent description out of a customer is
> not always an easy task. (I'm reading the word 'customer' here to include
> the case where there is no monetary relationship between the software
> author and the entity using it, but even when there is I think this still
> true.)
>

Just to be clear, I'm not saying it's unforgivable to occasionally
ship software with bugs.  It happens.

Compressing Javascript is sometimes necessary, but I believe that
often mangles named functions too.

To the the extent that your customer is running old software and
cannot always coherently describe tracebacks over a telephone, that
problem can be solved in the software itself, assuming an Internet
connection.  The software can capture the traceback and report back to
a server with the version number.

So, much of the argument against anonymous functions presented so far
is really orthogonal to whether functions are named or not.

Circling back to the original topic, Ruby blocks, I think there is a
misconception about how blocks are often used in Ruby.  Usually Ruby
blocks are inlined into a function and execute within that function.
Example:

    def print_numbers()
        [1, 2, 3, 4, 5, 6].map { |n|
            [n * n, n * n * n]
        }.reject { |square, cube|
            square == 25 || cube == 64
        }.map { |square, cube|
            cube
        }.each { |n|
            puts n
            raise 'problem here'
        }
    end

    print_numbers()

The bug that I inserted into the "each" block gets reported in the
traceback:

foo.rb:10:in `print_numbers': problem here (RuntimeError)
	from foo.rb:2:in `each'
	from foo.rb:2:in `print_numbers'
	from foo.rb:14

(I do prefer Python tracebacks BTW, but that is again orthogonal to
blocks vs. named functions.)

The blocks of code in the above Ruby code are somewhat analogous to
blocks of code in Python that happen within certain control
structures, such as "if," "while," "with," etc.  The extra
expressiveness of Ruby comes from the fact that you can act on those
blocks with your own method.  Of course, there is always a tradeoff
between expressiveness and simplicity.  I know the person who gave the
talk about Ruby vs. Python, and trust me, nine times out of ten, he
prefers Python's simplicity to Ruby's expressiveness.  But he likes
blocks.

I'm in the same boat.  I use Python a lot, Ruby less so, but when I'm
in Ruby-land, I actually enjoy the expressiveness of blocks.  They're
not particularly dangerous, and they allow you to express certain
sequential operations tersely and sequentially.  The contrived code
below maps numbers to squares and cubes, then rejects a couple tuples,
then maps back to cubes, then prints each of the cubes.

    def print_numbers()
        [1, 2, 3, 4, 5, 6].map { |n|
            [n * n, n * n * n]
        }.reject { |square, cube|
            square == 25 || cube == 64
        }.map { |square, cube|
            cube
        }.each { |n|
            puts n
        }
    end

IMHO there is no reason that I should have to name the content of each
of those four blocks of code, nor should I have to introduce the
"lambda" keyword.

I don't have a less contrived example handy, but the techniques above
apply often when you are filtering and massaging data.




More information about the Python-list mailing list