6 ms·
Sure, lambdas are not new, but there's still some interesting (maybe not worth calling them novel) ideas in Ruby's blocks: First of all the observation that mo
by judofyr 14y ago
Sure, lambdas are not new, but there's still some interesting (maybe not worth calling them novel) ideas in Ruby's blocks:
First of all the observation that most method only care about a single block. By having custom syntax for "method that takes a single block" you get a certain simplicity that makes methods look like built-in keywords:
loop { puts "Nuts" }
It does make multi-block methods look ugly, but frankly, I think they look ugly in any language.
Lisp has macros (which has both it's advantages and disadvantages), but I find that Ruby's blocks make the lack of macros more tolerable.
Compared to JavaScript/Perl/Python-lambas, Ruby's blocks don't introduce another scope. "self" and "return" still refer to the method's scope (I'm not sure what this type of scope is called; it's not the variable scope I'm talking about here). By introducing another set of keywords that work on the block ("next" and "break") you can easily wrap code in blocks without changing the semantics. See http://yehudakatz.com/2012/01/10/javascript-needs-blocks/ http://yehudakatz.com/2012/01/10/javascript-needs-blocks/ for more information about the difference between blocks and lambdas in object-oriented languages.
- snogglethorpe 14y agoBlocks, and the way they make it very easy to add new control structures "seamlessly", are indeed cool—but aren't ruby's blocks taken pretty much verbatim from smalltalk (which of course is also many decades old, if not quite as old as lisp)? Indeed, smalltalk seems a better example, as it uses methods and blocks to implement all control structures, even the "built-in" ones.
- masklinn 14y ago> aren't ruby's blocks taken pretty much verbatim from smalltalk Yes, with some crippling to fit them in a different syntax (Ruby's blocks are not first-class, so using more than one of them or methods on the blocks themselves isn't very nice) And amongst the important similarities are return being nonlocal from within blocks.
- pat_shaughnessy 14y agoI admittedly don't know a lot about Smalltalk - I was aware Ruby's (and every other language's) object oriented ideas were invented originally with Smalltalk, but I didn't know it had a block/closure syntax also. In this post I wanted to go back as far in time as I could :)
- vidarh 14y ago> In this post I wanted to go back as far in time as I could :) Then you might want to look at Simula as well. As much as the term "object oriented" was invented in conjunction with Smalltalk, a lot of the basic concepts (classes, objects...) came from Simula (but not closures).
- munificent 14y agoYou should check out Smalltalk blocks. They are much closer to what Ruby has than Lisp-style closures are.
- mseepgood 14y ago> but I didn't know it had a block/closure syntax also Blocks are one of the most important concepts of Smallalk. Every control structure is done via blocks in Smalltalk, even if/else is a method (message) on a boolean object, that takes two blocks as arguments: one for the 'true' case and one for the 'false' case.
- vidarh 14y agoProbably. In terms of features, Ruby is pretty much an unholy mix of Smalltalk, Perl, Awk with a dash of Lisp thrown in and a parser that's a monstrosity of epic proportions put on top (the MRI 1.8.x series had a Bison parser with 6k-7k lines of C to implement context-sensitive tweaks...) to make the whole thing have a syntax palatable for those of us who'd otherwise complain loudly about square brackets or parentheses... And I write this as someone who loves Ruby - there's much to love about Ruby, but a clean design is not it.
- pat_shaughnessy 14y agoLol - very funny. I have to agree you; as a Rubyist studying MRI internals there's a lot of very confusing code in there. And FYI Ruby 1.9/2.0 retains the confusing Bison grammar file. One of my goals with the eBook is to allow readers to get an understanding of how Ruby works without having to struggle through the confusing implementation themselves.
- diminish 14y agoBut do you agree that Ruby is an unholy mix? I would say it is a holy mix, Matz targeting programmer happiness, principle of least surprise etc. Speaking of internals, do you know enough internals of Smalltalk, python, Perl implementations to judge, Ruby is confusing compared to them? How would you compare them to Ruby implementions MRI and 1.9/2?
- pat_shaughnessy 14y agoNo - not the "unholy" part. Like vidarh I'm someone who loves Ruby (why else would I write a book about it!) but I do agree with him about the internal implementation: it's very confusing and hard to understand. At times, this leaks out into the syntax of an otherwise beautiful language. JRuby and especially Rubinius are great alternatives to MRI if you're interested in studying internals - they're much cleaner and easier to understand. Sorry I don't know anything about the internals of Smalltalk, Python or Perl and can't comment on them.
- pat_shaughnessy 14y agoThanks for the link to Yehuda's post - it was a great read. I love how he compared Javascript and Ruby, and it's intriguing what he said at the bottom about how JS might include "block lambdas" similar to Ruby in the future. Fascinating!
- masklinn 14y ago> First of all the observation that most method only care about a single block. Except when they don't, but if you can't build multi-block methods (or methods on blocks) because it looks like shit, you don't think about them. It's a form of blub, nothing more. There are numerous multi-block methods, Ruby's syntactic limitations just makes them unwieldy and thus avoided. Just as Python's or Java's syntactic limitations make higher-order methods unwieldy and thus avoided. > By having custom syntax for "method that takes a single block" you get a certain simplicity that makes methods look like built-in keywords Smalltalk stands in stark disproval of this assertion. > It does make multi-block methods look ugly, but frankly, I think they look ugly in any language. Eh? aCondition ifTrue: [ "true block" ] ifFalse: [ "false block" ] [ "some code" ] ensure: [ "recovery code" ] [ someObject aMessage ] on: aSignal do: [:sig | "action" ] I don't think that looks ugly. > Compared to JavaScript/Perl/Python-lambas, Ruby's blocks don't introduce another scope. They do, if you define a variable within the block it'll be local to the block. Ruby 1.9 even allowed shadowing variables from the parent method (which isn't possible in 1.8) > "self" [...] still refer to the method's scope So does it with the vast majority of languages. Certainly does in Python. Javascript behaves strangely with its somewhat-dynamically-scoped-but-not-really `this` but it's a peculiarity of JS, not Ruby. > and "return" still refer to the method's scope (I'm not sure what this type of scope is called) It's a non-local return, not really a scope.
- judofyr 14y ago> Smalltalk stands in stark disproval of this assertion. I stand corrected. Although Smalltalk solves it by introducing a special method call syntax (which is perfectly fine). I was mostly thinking about C-style-method-call-syntax when I wrote this (Perl, Python, JS). > They do, if you define a variable within the block it'll be local to the block. Ruby 1.9 even allowed shadowing variables from the parent method (which isn't possible in 1.8) I did mention explicitly that I wasn't talking about variable scope, so yeah, we're both correct :)
- prodigal_erik 14y ago"Dynamic extent" might be close to what you're talking about. That's what Common Lisp calls the hint that an object may be stack allocated because it's unused after the call.
- davidw 14y agoTcl uses blocks for pretty much everything, and they're not that bad looking: if { $something } { do this } else { do something else } The first one is actually an expression, but the do this and do something else are simply blocks of code passed to the 'if' command.
- arthurschreiber 14y agoWell, but Tcl blocks are nothing more than strings that you pass as arguments that then get eval'ed using uplevel. Ruby blocks are "real" language constructs, and are real closures, in difference to Tcl blocks.
- davidw 14y agoI can assure you that Tcl blocks are quite "real" in that they are code that gets executed. Initially, yes, they are treated as a string, but then they get byte compiled, and remain 'code' internally.
- draegtun 14y agoIts beauty (and power) comes with the consistency of the syntax ala Smalltalk / Lisp. PS. Here's an example of creating something similar with Perl: sub IF (&@) { my ($block, $flow) = @_; [ map { $flow->{$_} || sub {} } qw/else then/ ]->[ $block->() ]->(); } sub THEN (&@) { my $block = shift; my $flow = shift || {}; $flow->{then} = $block; $flow; } sub ELSE (&) { my $block = shift; +{ else => $block }; } IF { 1 == 0 } THEN { say "True" } ELSE { say "False" }; And with these being functions and not statements we can also do: my $result = IF { 1 == 1 } THEN { "True" } ELSE { "False" };
- icebraining 14y agoCompared to (...)Python-lambas, Ruby's blocks don't introduce another scope. "self" and "return" still refer to the method's scope 'self' in Python still refers to the method's scope too (if the lambda is inside one). class A(object): def b(self): self.name = "John" c = lambda: "Hello, %s" % self.name print c() A().b() #prints "Hello, John"
- TylerE 14y agoStrictly speaking that's merely a convention. Python simply has explicit method receivers. There's nothing pretty you from calling it `obj` or `foobaz` or whatever - it's just a normal variable.
- RHSeeger 14y ago> It does make multi-block methods look ugly, but frankly, I think they look ugly in any language Tcl... builtin 'for' for {set i 0) {$i < 10} {incr i} { puts $i } user proc 'myfor' myfor {set i 0) {$i < 10} {incr i} { puts $i } In some languages, they look exactly the same.
- osteele 14y agoThis is an excellent point — that Ruby embodies the syntactic insight that most functions that take any functional argument take only one functional argument. Frankly this turned me off from Ruby when Matz presented it at LL2 in 2002. My reaction was “this is an ugly hack that won't work with more than one funarg, by someone who doesn't really understand functional programming”. [I was coming from years of hardcore programming in Smalltalk and Common Lisp.] It was only years later that I realized this special case was so overwhelmingly the common case that it was reasonable to give it a nice syntax, and that there's a (big) place for programming that isn't in a “functional” style, but still makes liberally use of funargs. (Most Smalltalk and Lisp that I've seen occupies this middle ground too.) It reminds me of my first reaction to Python: “Meaningful indentation is a terrible idea!” Only a year or two later when I was writing Lisp pseudocode in a notebook [late '90's — no parenthesis highlighting or syntax-directed editor on my analog notepad] did I realize that what I was sketching used whitespace as shorthand, and was practically Python as it stood. That Ruby blocks have a weird scope, and that the syntax for creating and invoking closures outside of the block syntax is so cluttered, are unfortunate and seem mostly independent of having a compact notation for single-funarg function calls. Except that if, like Lisp and Smalltalk, all use of closures used the same syntax, this syntax would probably have become more polished.
- draegtun 14y agoI've always thought the func block list syntax in Perl [1] also had a strong influence on Ruby's blocks. Here's an example of map in both languages: # ruby double = [1..10].map { |x| x * 2 } # perl my @double = map { $_ * 2 } 1..10; NB. For more examples of the func block list syntax in Perl see grep and List::Util / List::MoreUtils modules. And it's easy create new ones as well: sub loop (&) { my $block = shift; $block->() while 1; } loop { say "Nuts" }; And you can also chain them together to build more complex constructs: sub ifTrue (&@) { my ($block, $cond) = @_; $block->() if $cond; $cond; } sub ifFalse (&@) { my ($block, $cond) = @_; $block->() unless $cond; $cond; } ifTrue { say "True" } ifFalse { say "False" } 1 == 1; I once wrote a lazy functional syntax in Perl using this approach :) [1] - Not sure what the proper name for this syntax is!
- majormajor 14y agoFor DSL/metaprogramming fun (and occasional hair-pulling, heh), Ruby also gives you instance_eval and its siblings/cousins for running a block with "self" bound to a different object. I've mainly used it for providing the ability to run a block in the scope of a certain object without the author of the block having to reference that object at all (so field names can be referred to without any prefix, which gives a nice clean syntax).
- pat_shaughnessy 14y agoYup - I'm planning to cover that later in the same chapter in my eBook. Your description is perfect: it's a closure with the "self" redirected to a different object (the receiver of instance_eval).