
Why Python doesn't need blocks - ceronman
http://stupidpythonideas.blogspot.com/2014/06/why-python-or-any-decent-language.html
======
jliechti1
Guido himself has said no multiline lambdas is an explicit design choice, and
it's not really a technical issue:

 _" But the complexity of any proposed solution for this puzzle is immense, to
me: it requires the parser (or more precisely, the lexer) to be able to switch
back and forth between indent-sensitive and indent-insensitive modes, keeping
a stack of previous modes and indentation level. Technically that can all be
solved (there's already a stack of indentation levels that could be
generalized). But none of that takes away my gut feeling that it is all an
elaborate Rube Goldberg contraption."_

source:
[http://www.artima.com/weblogs/viewpost.jsp?thread=147358](http://www.artima.com/weblogs/viewpost.jsp?thread=147358)

EDIT: This was from an earlier proposal in 2006.

~~~
ceronman
This is the only case where I think that using whitespace indentation for
delimiting blocks of code is not a good idea. It makes this particular problem
very difficult to solve elegantly. In languages where blocks are delimited by
brackets is easer.

~~~
angersock
...perhaps this is a hint that significant whitespace is a bad idea?

~~~
pyre
... like $[ in Perl?

~~~
draegtun
Fortunately $[ was disabled from v5.16.0 (released in 2012).

ref:
[http://perldoc.perl.org/perl5160delta.html](http://perldoc.perl.org/perl5160delta.html)

------
andrewvc
This is one of those pieces that is simultaneously very correct and very
wrong. Technically, it's mostly correct, but the fact is Ruby has blocks,
lambdas, and the ability to pass around a method attached to an object
instance. That gets you a superset of functionality really. It's just all
syntactic sugar.

As a lisper I could easily point my finger at ruby blocks and point out
they're more complicated than just always using a function, but the fact is,
in practice, it just doesn't matter. Ruby blocks cover 95% of the cases you
hit in a pretty way, and when they don't you can still use lambdas!

Now macros, those are a better solution to what blocks, but it's a 95%
solution, so I've stopped caring.

~~~
bryanlarsen
Sure, once you understand the differences between bound and unbound methods,
blocks, lambdas and Proc's. That's a lot to understand for something that's
fairly simple in many other languages.

~~~
andrewvc
You don't need understand that much. If all you know is "a block is just fancy
syntax for passing an optional lambda into a method", that will suit you just
fine. Does it capture 100% of the nuances of a block? No. But it's a fine bit
of functional knowledge. If you throw in calling Object#method(:foo) returns
what amounts to lambda {| _args| Object.foo|_ args| } you have 99% of what
you'll ever need.

------
ianbicking
This article doesn't seem to address continue, return, and break. These are
control-flow features that are often allowed in blocks (though not all
languages) that can't be done in a function. That is, in a block "return" will
return from the enclosing function, and continue/break are a sort of signal to
code calling the block. You can simulate the second, like jQuery.each(function
(item) {if (timeToStop) return false}); but I don't know of good conventions
for this so typically the functionality is skipped. But it's demonstrably
useful functionality because it is so widely present in languages.

------
otikik
It seems the author overlooked how `return` works in a block, compared to a
function. It was one of the main reasons why Yehuda Katz proposed adding
blocks to javascript (I see the reasoning behind it, but I think javascript
doesn't need more concepts).

~~~
msie
This. That the author doesn't understand this completely undermines the
article.

------
jameskilton
That's hardly fair to use the term "any decent language". Is the author really
saying that SmallTalk is not a decent language?

Of course Python actually _does_ have blocks (lambda), they're just very
limited, so it's a strange article none-the-less and to a non-Python user come
across as a bit of a hack so that you aren't constantly passing method
pointers around. But maybe that's just me.

~~~
ajanuary
Lambdas and blocks are different things.

------
stdbrouw
I think the author's correct here, there's really nothing uniquely wonderful
about blocks, but he's railing against people that don't exist. Pythonistas
would like some sort of way to do inline functions, beyond one-liner lambdas,
in Python. Whether those are just generic functions or Ruby-inspired blocks is
really not all that important. As long as we can inline 'em.

~~~
philipn
I don't understand this. You can just define a function anywhere in python and
use it (in local scope). I do this all the time, and it's a lot more clear
than an unnamed lambda function. What's the use case here?

~~~
stdbrouw
It's nice for chaining. E.g. in CoffeeScript:

    
    
      list
        .map (el) ->
          el * 2
        .filter (el) ->
          el % 2 == 0
    

I suppose in this case a list comprehension would work too:

    
    
      list = [el * 2 for el in list if el % 2 == 0]
    

... but as these things get more complex, inline functions become really
useful.

It's also nice for various DSL purposes, e.g. unit tests with Mocha

    
    
      it 'should be able to multiply a number', ->
        a = 2
        b = 3
        (product a, b).should.eql 6
    

Which in Python would become

    
    
      def test():
          a = 2
          b = 3
          assert product(a, b) == 6
    
      it('should be able to multiply a number', test)
    

Probably doesn't look that impressive, but once you're used to them from other
languages, it gets to be really frustrating to not have them in Python.

You also start noticing that Python has a couple of features that are actually
entirely unnecessary if only you'd have multiline inline functions. For
example, you could do away with decorators and `with` blocks.

    
    
      view = authenticate (req, res) ->
          res.send 'hello world'

~~~
jerf
"Which in Python would become ... assert product(a, b) == 6"

Python's got a lot more capacity for "DSLs" than it may immediately seem.
Writing "it.shouldBeEqual(product(2, 3))(6)" would be quite easy in Python,
actually; it's got all sorts of overloads and you can return all sorts of
excitingly overloaded values. You don't see it because it isn't considered
"Pythonic", not because Python can't do that.

(And I'm with Python on this. Those "fluent" APIs spend way too much effort
being cutesy and verbose and not enough effort being simple, transparent, and
composable. It looks good in the common case but when you write _good_ test
code it stops looking so good when you're writing map(it.shouldBe.eq,
testTable), and the fact that the cutesy "fluent" style takes even a baby step
in the direction of discouraging anything like that is an immediate
disqualification in my book. Oh, don't try to defend it by saying you can
still do the map I said... I've got enough experience in such things myself to
know that it's actually a _bigger_ problem than I'm making it out to be, not a
smaller one.)

~~~
stdbrouw
Kind of besides the point though -- that line in particular had nothing to do
with blocks/inline functions.

------
lloeki
> _Promises /A has been ported to Ruby, with basically the same API…_

This is, to me, the core of the problem.

> _The Promises /A spec from CommonJS defines Promise.then as taking three
> (optional) callbacks: fulfilledHandler, errorHandler, and progressHandler.
> The code above is passing two of them at once._

Shoehorning things from different realms (e.g desperately trying to write JS
in Ruby or vice-versa) is not a good idea.

Instead of either:

    
    
        db.select_one(sql, thingid)
            .then((proc {|rowset| rowset[0]['foo'}), (proc {|err| log_error(err)}))
    

or:

    
    
        db.select_one(sql, thingid)
            .then {|rowset| rowset[0]['foo'}
            .then(nil) {|err| log_error(err)}
    

Why not do:

    
    
        db.select_one(sql, thingid)
            .then {|rowset| rowset[0]['foo'}
            .else {|err| log_error(err)}
            .each {|whatever| the_progress(callback_is)}
    

Also, _method(:log_error)_ will happily turn the method into a proc.

This last case is actually extremely important. Contrary to what the author
claims, functions and methods _are_ first class in Ruby. The problem is that
in Ruby's grammar an identifier without parentheses is a method call if that
identifier does not resolve to a variable. Therefore, you cannot refer to an
existing function by its name, merely because it would resolve to a call.

Therefore the difference between those is _purely syntactic_ :

    
    
        Python  Ruby
        foo     method(:foo)
        foo()   foo
    

Also, this[0] explains how _& :to_i_ works, which can be leveraged with great
success, should one implement the promise class in a sufficiently idiomatic
way (turning the above _log_error_ reference into &:log_error)

[0]: [http://ablogaboutcode.com/2012/01/04/the-ampersand-
operator-...](http://ablogaboutcode.com/2012/01/04/the-ampersand-operator-in-
ruby/)

------
tbrownaw
...huh? Having piece-of-code A call piece-of-code B which in turn calls some
other piece of code _as determined by A_ is useful. Whether you call this idea
a "block" or a "lambda" or a "function pointer" or a "first-class function" is
an implementation detail.

------
dkarapetyan
Next up: Why we don't need higher level abstractions because we can do it all
in assembly.

------
anaphor
Python already has multi-line lambdas, you just need to be creative
[http://ideone.com/ybf63a](http://ideone.com/ybf63a)

------
true_religion
If anyone has used block-happy smaltalk, you know how amazing it can be to do
dependency injection easily and arbitarily. Almost function you write can be
generic, and only become specific when it needs to in the business code.

------
mpweiher
Hmm...I am not sure what difference the author is making between closures and
blocks, the terms at least overlap substantially.

For example, Smalltalk blocks have been "upgraded" to closures in most
dialects (and not implementing them as closures in the first place was not a
choice of semantics, but one of implementation simplicity).

Also, Swift closures _are_ the same as Objective-C blocks.

    
    
       +(void)logMe: (void (^)(void))block
       {
           NSLog(@"object of class: %@",[(id)block class]);
       }
    

Called from Swift with a closure:

    
    
        func applicationDidFinishLaunching(aNotification: NSNotification?) {
            logger.logMe( { } );
        }
    

Results in:

    
    
        2014-06-30 19:55:37.973 SwifTest[94782:303] object of class: __NSMallocBlock__
    

So much ado about nothing?

------
fdsary
I was a ruby developer, who then had to do a lot of C and am writing
CL/Clojure for fun.

Then I went back to a ruby project, and realised - the language I used to
adore kinda sucks, because it's _so_ un-functional.

------
Arnor
> JavaScript, for example...

What an odd appeal to authority...

