Summary:
Yukihiro Matsumoto, the creator of the Ruby programming language, talks with Bill Venners about two kinds of nameless functions in Ruby, blocks and closures.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: January 3, 2004 10:31 AM by
rubyfan
|
Yukihiro Matsumoto, the creator of the Ruby programming language, talks with Bill Venners about two kinds of nameless functions in Ruby: blocks and closures. Read this Artima.com interview with the creator of Ruby: http://www.artima.com/intv/closures.htmlWhat do you think of Matz's comments?
|
|
|
I'm interested in why blocks are so widely known as "nameless functions". It just strikes me as curious when a thing is defined in terms of what it isn't, or what it doesn't have (in this case, a name). What is so important about blocks not being named? What would be different about the language if, say, only named functions could be given to loops, etc.? Or, what is the more general term or concept that encompasses both named and nameless functions? What would a language be like if it were built around these things instead of imposing the distinction of namedness on them?
|
|
|
The simplest benefit is that you do not have to try to keep thinking up meaningful unique names for blocks of code that are only used in one place and that the code exists where it is used rather than defined in a procedure / function 100+ lines before or after the point that it is used. When the blocks are quite small this shrinks the code as you are not writing loads of 'structural'(?) code. Additionialy blocks dispense with the need to create surrogate variables that the programmer is not interested in, for example: h.each_key {|k| puts h[k] }
Here h is a hash and the code gived iterates over the hash passing the key into the block to be processed - in this case used to print out the value. Note that the only variables involved are the hash and the key. Try rewriting that in Java without introducing any more 'structural' variables. Also hash also has this iterator: h.each_pair {|k,v| puts v }
which returns the key and the value. You can probably guess what h.each_value does. You can also extend (or subclass if you wish) the hash class to provide any number of iterators you desire.
|
|
|
why blocks are so widely known as "nameless functions" Often called 'anonymous functions' - simply that they are functions that don't have a name. More interesting to ask why they are also called lambda functions, see the informal description: http://en2.wikipedia.org/wiki/Lambda_calculusterm or concept that encompasses both named and nameless functions? Functions. What would a language be likeThat's what functional programming languages are like - name functions and anonymous functions are used interchangeably.
|
|
|
the first reason is to respect the history of LispHow strange then to talk of blocks (like this Smalltalk) rather than lambda dictionary keysDo: [:key| *do something with key* ]
dictionary keysAndValuesDo: [:key :value| *do something with key and value* ]
In an OO language like Smalltalk, all the 'named functions' are object methods - they are message sends to some object. In contrast, Smalltalk blocks are standalone functions.
|
|
|
The really powerful thing about anonymous closures (functions or blocks) is not so much that they are anonymous, but that they can be used as expressions. This means that I can say things like "the addition of 5 to something" just as easily as I can say "5". I don't have to announce beforehand that "when I say 'add5', I mean the addition of 5 to something", and that's a good thing, because "add5" isn't a particularly reusable function anyway, so why name it? I'd rather avoid the naming issue altogether and say "map the addition of 5 to every number in this list". That's one thing you can do with anonymous closures.
Another nice feature of anonymous closures is that I have more control over the order of operations. Compare (not Ruby code, because I'm still getting my feet wet with Ruby myself):
function do_it() { print 'doing it' }
// call do_it after 500 ms after(500, do_it)
with:
after(500, function() { print 'doing it' })
In the second case, it says what I mean. After 500ms, print "doing it". This is a very straightforward and convenient way to do asynchronous / event-driven programming.
|
|
|
> Another nice feature of anonymous closures is that I have > more control over the order of operations Seems like this is the same with named or anonymous functions (in Nice http://nice.sourceforge.net/index.html ) void after(int msDelay, void->void fun){ fun(); } void main(String[] args){ // named function after(msDelay: 500, fun: print_doing_it); // anonymous function after(msDelay: 500, fun: ()=>{ println("doing it 2"); }); } void print_doing_it(){ println("doing it"); }
|
|
|
> > Another nice feature of anonymous closures is that I > > have more control over the order of operations > > Seems like this is the same with named or anonymous > functions (in Nice http://nice.sourceforge.net/index.htmlRight, but the point I was trying to make was that by supporting closures as expressions you can say "after this, do that" instead of "after this, do that thing which I defined somewhere else". But it looks like Nice supports both. Nice is nice. =)
|
|
|
by supporting closures as expressions you can say "after this, do that" instead of "after this, do that thing which I defined somewhere else"We can create an expression that will apply a function after a specific delay, and hold that expression in a variable and invoke it a little later: void->void delayedFunction(int msDelay, void->void fun){ return ()=>{ after(msDelay, fun); }; }
void main(String[] args){ let f = delayedFunction(msDelay: 500, fun: do_it); f(); f(); }
void do_it(){ println("doing it"); }
void after(int msDelay, void->void fun){ fun(); }
Yes, Nice is nice, and has first class functions (both named and anonymous).
best wishes, Isaac
|
|
|
> Another nice feature of anonymous closures is that I have > more control over the order of operations. Compare (not > Ruby code, because I'm still getting my feet wet with Ruby > myself):
well, I suppose it is: after(500) do puts 'doing it' end
And I agree that this is more readable :)
BTW, some more cool usages of blocks:
- to pass a 'discriminating' function, like in Enumerable.sort()
array_of_persons.sort { |p1,p2| p1.name <=> p2.name }
or, even better, in sort_by() (that internally does a shwartzian transform)
array_of_persons.sort_by {|p| p.name}
- for threads: Thread.new { do stuff}
- for event/signals/callbacks: at_exit { puts "existing now.."} or button.onclick { puts "clicked"}
|
|
|
Are there new uses that didn't exist in Smalltalk?
Here are some Smalltalk equivalents:
> - to pass a 'discriminating' function, like in > Enumerable.sort() > > array_of_persons.sort { |p1,p2| > p1.name <=> p2.name > }
Smalltalk has a special SortedCollection, so we might say array_of_persons asSortedCollection: [:p1 :p2| p1 name < p2 name ]
or for an in-place sort on the array SequenceableCollectionSorter sort: array_of_persons using: [:p1 :p2| p1 name < p2 name ]
> or, even better, in sort_by() (that internally does a > a shwartzian transform) > array_of_persons.sort_by {|p| p.name}
'shwartzian transform' this caching optimization isn't built-in to Smalltalk.
> - for threads: > Thread.new { do stuff}
t := [ do stuff ] fork.
or more interestingly
p := [ do stuff ] promise.
> - for event/signals/callbacks: > at_exit { puts "existing now.."} > or > button.onclick { puts "clicked"}
Object when: anEventNameSymbol do: aBlock
(More often in the Event System a specific method symbol is used rather than aBlock.)
In Smalltalk even conditionals and loop statements are defined with blocks:
i<5 ifTrue: [ do something ]. [ i<5 ] whileTrue: [ do something ].
|
|
|
Ofcourse Matsumoto has done some impressive stuff in Ruby but I must say I think the way the keyword 'yield' works is not a good design. I have to scan the implementation of a method to find out wether the method needs or supports a block or closure. I'd much prefer an interface declaration of the method that specifies clearly that it has an (optional) argument that is block or closure.
And since I now have spoiled the party of Ruby adulation I might as well add that I find Ruby too lacking in documentation to be workable. Probably that's better in Japanese but then english is the lingua franca of software development. Maybe they think that the source is the documentation but then you can't use an interface as an abstraction and thus reduce complexity. Theoretically you'd have to analyze all the code that your program uses, all the libraries down to the C-level, to find out wat it does. That's clearly not in tune with the weaknesses and strengths of humans. It's letting the human adapt to the computer. Ofcourse there's the book by Thomas Hunt but I think API documentation, "what's new" etcetera is a requirement for a release of a serious programming language.
As a hobby language it's nice to play with Ruby for a few hours. But I'll use Python if I really need a scripting language for something. They're documentation is exemplary.
Happy Newyear, Joost
|
|
|
I think the way the keyword 'yield' works is not a good design. I have to scan the implementation of a method to find out wether the method needs or supports a block or closure.If a method takes a block the last argument in the parameter list will be preceeded by a '&': For example (not a very useful example, I admit): def call_block(param,&b) b.call(param) end
call_block("Hello") { |p| puts p } #=> "Hello"
So you could do it this way instead of using yield (the two methods are essentially equivilent) and you could tell by looking at the method's param list that it takes a block. And since I now have spoiled the party of Ruby adulation I might as well add that I find Ruby too lacking in documentation to be workable. The pickaxe book ( Programming Ruby by Dave Thomas and Andrew Hunt) is quite a good reference to get you started. Sure it doesn't have info on some of the newer included libraries (like REXML or YAML) but you can usually find docs on the newer items at: http://www.ruby-doc.org/I think API documentation, "what's new" etcetera is a requirement for a release of a serious programming language. See: http://www.ruby-doc.org/stdlib/What else are you looking for? As a hobby language it's nice to play with Ruby for a few hours.Gee, nobody ever told me that Ruby was a 'hobby' language. I wonder how I managed to get all that serious work done with a 'hobby' language? ;-) Oh, well, you can't please all the people all the time.
|
|
|
Phil, I knew that there is another way in Ruby to handle closures. I think the argument you mention is called a 'proc' object. My point was that the construction with 'yield' is bad design. You will notice that Matsumoto takes the 'yield' approach as the way to explain it. I think it is somewhat the dominant approach in Ruby. So you will not be able to conclude on the basis of the interface that the method does not take a block or closure since there is a construction, that is often used that doesn't show up until you read the code. I think it is a redundant construction that wastes a whole language keyword while the other construction is also more object-oriented. It does not follow Matsumatos principle of least surprise. If you're from the planet Perl you'll think that's a feature of course. :-)
Concerning the documentation: the documentation of the standard lib has been set up 2003-11-23 so that wasn't there when I worked with Ruby in June and July. I remember that I ran rdoc over the stdlib and just found empty html. Looking at the dates in code comments I see that a lot of them have been added around november and december this year. Apparently people had the same opinion that it won't do without uptodate API docs.
I was very disappointed when I found the empty rdoc html, so this is a great improvement. Thanks for mentioning it. I had't noticed.
My 'hobby language' comment was a bit provocative I admit. My point is just that I can not deliver code to a customer on a platform that has no uptodate documentation. Especially since Ruby is an obscure language. With 'obscure' I mean that not a lot of developers are proficient in it. In the Netherlands at least. I'd be throwing a heap of unmaintainable code on customer. So in that way I think that a language implementation that does not document it's standard libs and keep this uptodate is not fit for professional, commercial work. If you're just writing code for yourself it's no problem of course. (If you want to invest the time)
Anyhow, Groeten, Joost
|
|
|
So in that way I think that a language implementation that does not document it's standard libs and keep this uptodate is not fit for professional, commercial work. If you're just writing code for yourself it's no problem of course. (If you want to invest the time)Again, there is plenty of documentation of Ruby's standard libs. Most of us use the pickaxe book (Dave Thomas and Andy Hunt's Programming Ruby book), there is online documentation at http://www.ruby-doc.org. Other resources are available at http://rubygarden.org (a wiki) and http://www.rubyforge.net. There are many of us doing "professional, commercial work" with Ruby. I am currently doing some for a very big, well known, household name company and there are several others in the same company using Ruby. And this isn't just new work in the last few months, it's been going on for a couple of years now. If, as you alledge, there is "no uptodate documentation" for Ruby's libraries, then how did we manage to get so much done in the language in a commercial setting? Especially since Ruby is an obscure language. With 'obscure' I mean that not a lot of developers are proficient in it.True, Ruby isn't as well known as Perl,Java or C++. However, when I need to bring a new developer up to speed with Ruby I give him/her a copy of the pickaxe book and if they're reasonably bright and have some background in OO programming they're starting to be productive in a couple of days and after a week or two they are making real contributions. So sure, it's not as common a language, but things tend to make sense and much of the standard lib is very intuitive without even having to reach for the documentation (which is available if you need it). I've heard many times from people that they consider Ruby to be their most productive language even after only a couple of months using it. See: http://www.rubygarden.org/ruby?RealWorldRubyFor more stories of Ruby being used in commercial, government and academic settings.
|
|