6 ms·
Why Python doesn't need blocks
- jameskilton 12y agoThat'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 12y agoLambdas and blocks are different things.
- tbrownaw 12y ago...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.
- Arnor 12y ago> JavaScript, for example... What an odd appeal to authority...
- andrewvc 12y agoThis 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 12y agoSure, 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.
- angersock 12y agoSurely that's not really that difficult to understand?
- andrewvc 12y agoYou 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.
- mcphage 12y ago> That's a lot to understand for something that's fairly simple in many other languages. That's what's so nice about how Ruby does it; you don't need to understand any of that to use blocks. As you learn the language, you learn those things, but none of them are required to get started.
- lloeki 12y ago> It's just all syntactic sugar. I disagree. Blocks are the fundamental building blocks (!) of various constructs that are derived from them. They're 'thinner' than lambdas, procs and methods. Blocks alone are only part of a system, which, together with binding allows one to build lambdas, iteration constructs, and various other features. Comparing them head-on to functions or lambdas therefore makes no sense. For example, where Python has 'hardcoded' generator expressions, a Ruby developer can leverage blocks to make his own generators. The same goes for features such as with, which are syntactically defined in Python, but which are customary to simply implement with blocks in Ruby. Combining instructions such as next and continue with yield return values and arguments, along with rebinding allows one to define new constructs and syntaxes without paying the price of Lisp's purity (which basically mandates that the language is made of sexprs).
- jliechti1 12y agoGuido 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.
- stdbrouw 12y agoDoesn't make it any less annoying to not have them.
- munificent 12y agoLanguage design, especially syntax design, is always a series of trade-offs. Aside from relatively rare outright mistakes (bitwise precedence in C, associativity of ?: in PHP), most syntax choices that people don't like are done to make something else easier. I really like blocks, but Python deliberately makes it harder to support those in return for not needing scope delimiters in the majority of code that isn't using blocks. It seems like a reasonable choice to me.
- BugBrother 12y agoBlocks could be allowed in one liners. That would make Python a credible solution in that use case. (And yes, not all one liners need it. I know.) And list comprehensions seems like a kludge from a lack of real lambdas (map). So that is an extra concept. Edit: Ruby doesn't have closures?! Edit 2: 2nd paragraph of the linked article: "In Ruby, Objective-C, and C, functions are not first-class objects, they don't capture closures, and they can't be defined inline." For Ruby, that don't claim there are no closures at all. Got it.
- fdsary 12y agoI 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.
- anaphor 12y agoPython already has multi-line lambdas, you just need to be creative http://ideone.com/ybf63a http://ideone.com/ybf63a
- stdbrouw 12y agoI 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 12y agoI 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?
- anaphor 12y agoPart of the reason why functional languages like Scheme have anonymous functions (lambdas) is so that you can encode various language features in them. function definition: (define (f x) (+ x x)) => (define f (lambda (x) (+ x x))) variable binding: (let ((x 1) (y 2)) (+ x y)) => ((lambda (x y) (+ x y)) 1 2) delayed evaluation: (delay (display "foo")) => (lambda () (display "foo")) and lots more... But they also sometimes are convenient when you have a function or procedure that expects the same as one of its parameters, but said function doesn't already exist, and there isn't a simple composition of higher-order (functions that take or return functions) functions that does what you want. For example, say you wanted to multiply a list of numbers by their squares (this is in Racket): (define (sqrmult n) (foldr (lambda (x y) (* (sqr x) (sqr y))) 1 (stream->list (in-range 1 n)))) See you don't have to name the function?
- stdbrouw 12y agoIt'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'
- otikik 12y agoIt 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 12y agoThis. That the author doesn't understand this completely undermines the article.
- dkarapetyan 12y agoNext up: Why we don't need higher level abstractions because we can do it all in assembly.
- ianbicking 12y agoThis 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.
- mpweiher 12y agoHmm...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?
- lloeki 12y ago> 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-in-ruby/ http://ablogaboutcode.com/2012/01/04/the-ampersand-operator-...
- true_religion 12y agoIf 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.