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/ for more information about the difference between blocks and lambdas in object-oriented languages.
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.
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 :)
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.
# 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!
Indeed, smalltalk seems a better example, as it uses methods and blocks to implement all control structures, even the "built-in" ones.
And I write this as someone who loves Ruby - there's much to love about Ruby, but a clean design is not it.
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.
In this post I wanted to go back as far in time as I could :)
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.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" };'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"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.
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.Programming in Lisp is like programming in assembler. Reading it is worse. The learning curve is tremendous.
Languages serve lots of demographics, not just comp-sci wonks.
Now, they are "wonks" for knowing a slightly different (and demonstrably effective) style of programming.
there's nothing magical about lisp compared to other languages. to read it you don't need a cs degree and you don't need vast levels of intelligence - you just need practice. after a while it becomes quite clear.
you're free to think whatever you want, of course, but this kind of negative attitude - "it's for the smart people, not me" - just means that you lose out.
lisp itself is not that far from a language like python (or, i guess, in the context of this thread, ruby) once you get used to it. it's really not rocket science. all you're doing here is harming yourself.
>> The learning curve is tremendous.
Try "Land of Lisp"
http://www.amazon.com/Land-Lisp-Learn-Program-Game/dp/159327...That's not quite accurate. Common Lisp and Clojure are both lexically and dynamically scoped. In CL, things created with def* (defun, defparameter, defvar, etc...) are dynamic, while function arguments and things created with let are lexical[0]. In recent versions of Clojure, it's necessary to explicitly make a symbol dynamic with metadata.
[0] This is probably not entirely correct or complete, but should get across the general idea.
In fact, the computer science concept behind blocks, called “closures”
As far as I know, the language-free concept of "block" is orthogonal to the concept of scope. That is, a block is not by necessity a closure.The problem is almost everyone I talk to in the startup scene thinks that an anonymous function/block == a closure.
Ruby and Java saw their first public releases within a month of each other.
That way, a class of errors becomes impossible; namely, those errors that involve returning from a block whose method has already returned. I would have closures in the language but they would be just like Scheme closures: they would be independent of their creation context except for lexical bindings. I would compile blocks right into the method in which they appear.
Unfortunately, I don't have experience with any language that uses blocks. I'm quite familiar with closures but not blocks. I'm wondering if any Ruby or Smalltalk (or ...) programmer can give some reasons why blocks ought to be values.
Also, (referring to the post) does anyone know why rb_block_t was not embedded in rb_control_frame_t? That's the usual idiom in C and I can't think of a reason not to use it.