Wrong. Blocks are not a feature, they're a workaround for three serious problems: In Ruby, Objective-C, and C, functions are not first-class objects, they don't capture closures, and they can't be defined inline.
JavaScript, for example, doesn't have any of those problems. You can define functions inline, they capture from the local scope, and they're first-class objects that can be passed around. And its functions can do everything blocks can, and more.
This is why, after all the work Apple put into adding blocks to ObjC and C, their new language Swift doesn't have them. It doesn't need them, because it has first-class closures that are inline-definable. (They did add some syntactic sugar to make passing functions around look as clean and compact as using blocks in Ruby, giving them the best of both worlds, but practically it's a pretty minor difference, and I think the language would still be perfectly readable without it.)
Python has a different problem from Ruby, ObjC, and C. It has real first-class closures that are inline-definable—but doing so means you can only use single-line expressions as the body, not statement suites.
Why functions are better than blocks
One concept is better than two
Procs (the things that blocks define) and functions are very different kinds of things. This means that anyone learning Ruby has to learn two different abstractions for callable things, instead of one. And you have to keep learning twice as much no matter how far you go—even when you get into the internals of the implementation, there's a proc stack on top of the function stack and they work differently.As another consequence of functions and procs not being the same thing, you can't just pass a function to someone expecting a proc; you have to wrap it up in a block. So, if you realize you need the same callback in two different places, or it just gets too big and messy, you can't just refactor it into an out-of-line function the way you would (trivially) do in JS. Of course you can get around this by wrapping the function in a block that just accepts the same parameters and forwards them to the function, just so you have a block to pass and call, but you don't have to do that in other languages.
Being limited is limiting
The fact that blocks have to be the last argument to a function call immediately rules out any APIs that take a callback plus some optional keyword-only arguments, or two callbacks, etc. Like, say, all of the constructors in Tkinter. You can get around this by wrapping your blocks in proc expressions, but again, you don't have to do that in other languages.Here's a real-life example (slightly simplified) from JavaScript:
db.select_one(sql, thingid) .then(function(rowset) { return rowset[0]['foo']; }, log_error);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.
Promises/A has been ported to Ruby, with basically the same API… but to actually use it, you have to write code like this:
db.select_one(sql, thingid) .then {|rowset| rowset[0]['foo'} .then(nil) {|err| log_error(err)}… or like this:
db.select_one(sql, thingid) .then((proc {|rowset| rowset[0]['foo'}), (proc {|err| log_error(err)}))We had to either make two calls to then, to allow both handlers to be the last argument, or we have to explicitly wrap them in proc expressions (which I may not have done right; I'm a bit rusty…). And we had to wrap the log_error function (which we already had sitting around, because we have 43 errbacks that just do log_error and another 7 that do some other stuff before/after log_error) in a pointless forwarding block (43 times). I think it's pretty clear that either way, the JS is a lot more readable and concise, despite JS's generally uglier and more verbose syntax, and that the limitations of blocks are the reason.
Why blocks are better than functions
This one is easy: they aren't.Aren't they more compact?
Compared to JS functions, yes, Ruby blocks are more compact and less boilerplatey. But all that proves is that JS is a verbose and ugly language. Compared to almost any other non-block-using language, the advantage disappears:numbers.map {|x| x*2} # Ruby block map(function(x) {return x*2;}, numbers); // JS function map(lambda x: x*2, numbers) # Python lambda numbers.map {x in x*2} // Swift closure numbers.map {$0*2} // Swift closure again map (\x -> x*2) numbers -- Haskell lambda map (*2) numbers -- Haskell operator section
What about writing flow control statements as functions?
You can do that with inline functions too. In some languages, it's clumsy or verbose, but again, that has nothing to do with blocks vs. functions, and everything to do with the syntax of those languages.Here's the paradigm case of a Ruby block as loop statement:
# Ruby block
10.times do |i|
print "#{i}"
end
// JS function, assuming you added a Number.times method
10.times(function(i) {
console.log(i);
});
# Swift function, assuming you added an Int.times method
10.times {i in
println(i)
}
-- Haskell lambda, assuming you wrote a times function
do 10 `times` \i
putInt i
What about return-through (and break and continue)?
As Ary Borenszweig points out in a comment, in Ruby, the normal return statement in a block (unless wrapped in a lambda statement) returns from the calling function, not just the block. And likewise, break and continue can affect a loop in the calling function, around the block.While there are idiomatic uses for this (and some common abuses, too) in Ruby, almost all of them can be handled by just using another function in other languages. Let's take his example:
def foo
10.times do |i|
return true if i == 1
end
false
end
puts foo # true
How would we write this in Python? The obvious way won't work; when the inner function returns True, it's just returning to foo, not returning through foo to its caller.
But times, as written in the obvious way, returns a lazy iterator of each of the values. So, all we need is a function that short-circuits on the first truthy value, returning something falsey if none of them are truthy. And that function is already built in:
def foo():
return any(times(10, lambda(i)): i == 1)
print(foo()) # True
It's hard to see how this is overly verbose or harder to read than the Ruby equivalent. (And the idiomatic version, any(i==1 for i in range(10)), is even simpler.)
This would admittedly be hard to write in standard ECMAScript (because there's no built-in support for laziness, iterators, etc.), but it's easy to write in C++, Haskell, Swift, or even Mozilla-specific JavaScript. So, that's just a limitation of one language, not a limitation of functions in general. (And likewise, a more complicated case would be harder to write in Python because you can't use statements in a lambda expression, but that doesn't apply to JS, C++, Haskell, Swift, etc.; it's also just a limitation of one language.)
But you can't do that for everything!
There are some cases that are harder to map to a higher-order function or iterator function. So, how do you deal with those cases?You can fake return-through with exceptions. This has the advantage that it lets you return through exactly as many functions as you want, rather than through all blocks and no functions. It has the disadvantage that it's clunky:
def throw(e): raise e
class ReturnError(Exception): pass
def foo():
try:
times(10, lambda(i):
throw(ReturnError(True)) if i == 1 else False))
return False
except ReturnError as e:
return e.value
You can factor most of that out if you really want to make it less verbose. If you're interested, there are dozens of blog posts showing how to do this in Python, C++, etc.
But you'll notice that people rarely do this in real-life code. Because it's almost always easier to find the right higher-order or iterator function that makes it simpler. And most of the cases where it isn't are as confusing in Ruby as they are in other languages. The cases where return-through would actually be helpful in making code more readable are rare enough that people prefer to just write things out verbosely in those cases than resort to something that their readers will have to think through, like returns wrapped in exceptions. And if they're not common enough to be worth adding extra complexity to people's programs, they're probably not common enough to be worth adding extra complexity to the language (again, this requires two kinds of callables, two kinds of returns, two kinds of stacks—twice as much for people to learn).
So why do people want blocks in Python?
As mentioned at the top, the real problem people are trying to solve is the lack of multiline lambda. In JavaScript or Swift, a function defined inline inside an expression can contain statements, and span multiple lines. In Python, it can only contain expressions.People suggest that Python should have multiline lambdas all the time. And if it did, we'd have everything anyone wants out of blocks. (Well, they might also want a briefer syntax, but people can work on that separately—see Fixing lambdas for more on that.) But it turns out to be very tricky to come up with a clean way to escape from the middle of an expression, introduce a statement block, and then return to the expression. Many of the best minds in the Python community have tried and failed to solve this problem.
It's conceivable that, because of the very fact that blocks are strictly weaker than functions, it might be easier to come up with a multiline block syntax than a multiline lambda syntax. So far, nobody's come up with a way to take advantage of that fact to offer a concrete solution, but I can see why people have hope.
The most likely place to hang your hope is the fact that blocks have to come last, and go outside the parentheses, which means that you might be able to avoid that "… and then return to the expression" part, and that might be where all the trouble is. But taking an example from Swift, you can apply the exact same syntactic sugar to function definitions that happen to appear in the same place blocks would appear, while not allowing it in other places. So, why not just do that instead of adding blocks?
A concrete proposal
This isn't really a serious proposal, it's just mean to illustrate that whatever you could do to add blocks could probably be just as easily modified to add multiline lambdas in similarly-restricted positions. So, bearing that in mind…From Swift, borrow the idea that if the last argument to a function call is a lambda, and there's nothing else after that function call in the current expression, we can move it out of the parentheses.
Similarly, if any keyword argument to a function call is a lambda, and there's nothing else after that call, we can move the lambda out of the expression an replace it with a * inside the parens.
(It might be possible to make things less verbose—e.g., leave off the lambda keyword—but again, that can be solved separately.)
And if that lambda is the last thing on the line, instead of following the colon with an expression, you can follow it with a suite. So, you could do things like this:
btn = tkinter.Button(master, txt="Send", command=*) lambda: msg = vmsg.get() vstatus.set("Sending {}...".format(msg)) d = conn.send_msg(msg) d.addCallback() lambda(result): vstatus.set("Sent!") d.addErrback() lambda(failure): failure.trap(SpamException) vstatus.set("Failed: {}".format(failure))
Would we really want this anyway?
There are people who believe that, even if there were a good syntax for multiline lambdas that fit into Python perfectly, it would be better not to add it, because it would be an attractive nuisance. While there is definitely code that could be improved by moving a one-shot function definition inline, there's a lot more code that could be made much less readable by doing the same thing, and it might be very tempting to do so. And there's a lot of JavaScript code out there, in particular, that shows how many people might succumb to that temptation.The argument that blocks would all people to write custom flow control statements scares a lot of Python users even more. The fact that Python has a small number of immediately-recognizable compound statements, and everyone can remember which ones do and do not control flow, it a good thing; it makes it a lot easier to get an overview of the program by scanning the source.
Personally, every time this (either blocks or multiline lambdas) comes up, I'm more convinced that PEP 403 is a more general and more Pythonic solution to the problem. Here's the example above written with PEP 403 @in clauses:
@in btn = tkinter.Button(master, txt="Send", command=on_send) def on_send(): msg = vmsg.get() vstatus.set("Sending {}...".format(msg)) d = conn.send_msg(msg) @in d.addCallback(sent) def sent(result): vstatus.set("Sent!") @in d.addErrback(failed) def failed(failure): failure.trap(SpamException) vstatus.set("Failed: {}".format(failure))It's a bit more verbose, but it has the massive advantage that the functions are defined with exactly the normal def statement. And it works with functions that aren't the last argument, and it works with classes, and it gives the functions names that can be used in tracebacks (but without leaking those names into the outer scope), and so on.
It still can only be used on one function in an expression, ruling out Promises/A-like APIs, but blocks, or multiline-lambdas-only-in-block-ish-contexts, have the same problem, so it's not any worse.
View comments