|
|
|
SearchCategories
Books by the AuthorOther Ruby Projects |
Recursion and Callbacks
Posted over 2 years ago
in Higher-Order Ruby.
Note: This is the first article in my Higher-Order Ruby series. I'm currently reading through Higher-Order Perl, by Mark Jason Dominus. (Yes, I read books about things other than Ruby.) So far, I'm enjoying the title quite a bit. It certainly has me thinking and the Perl in it is very clean and easy to understand. That helps me translate the concepts to my language of interest. I'll post some of my Ruby translations of the books example code here as I go along. Others familiar with the book might enjoy looking over them. Be warned, my comments might not make much sense to those who haven't read the book. RecursionThe book starts with some very simple recursion examples trivially translated. Here's one for manually translating
Here's another for calculating factorials:
MJD does talk about how recursion can always be unrolled into an iterative solution. That leads to what is probably a more natural Ruby solution for examples like these. Here's how I would probably code
Of course, that's just not what this chapter is about. CallbacksThis section starts off by showing just how cool Ruby's blocks can be:
Interestingly though, MJD quickly gets into passing around multiple anonymous functions, and Ruby has to do that just like Perl:
The above makes me want for a multi-block syntax. It's probably worth noting here that the above is basically an attempt at direct translation. I am aware of the standard Variable ScopeTwice in this chapter, MJD has to pause the discussion to school the reader on the dangers of using improperly scoped variables, which can easily wreck recursion. What I found interesting in reading these was that coding the correct Perl was always adding a step, but it Ruby you have to add a step to get it wrong. What Rubyists naturally want to type turns out to be the correct thing to do, at least in these cases. I think this is a good sign that Ruby got variable scope right. Functional vs. OO ProgrammingWe always talk about how Ruby is very Object Oriented, but after reading this side discussion about the two approaches, I wonder how true that really is. The way MJD described OO, is certainly not how a lot of idiomatic Ruby comes out. And I found myself seeing a few of the described Functional Programming elements in my favorite language. Perhaps Ruby is a hybrid Functional OO Programming Language? Food for thought... |
|
|
|
The way MJD described OO, is certainly not how a lot of idiomatic Ruby comes out.
Could you elaborate on this? Sounds interesting.
MJD is mainly discussing how callbacks can make code so much more powerful. I'm paraphrasing here, but first he describes the functional style of handling callbacks which basically comes out to: Have your workhorse routine accept functions as arguments and call them as needed. Then he describes how you do the same thing in object oriented languages (paraphrasing again): Build a class where the main work method calls virtual methods so user code can subclass and provide the missing methods.
While you certainly can write Ruby code this way, I sure don't see much of it. I think we tend to hang this kind of functionality on Ruby's blocks, which is a lot closer to passing a function argument I would say. Even when we do have people subclass and provide methods, there is often some magic involved (like how
Test::Unitspots all methods beginning withtest_).It just strikes me that we use objects pretty differently from typical object oriented languages.
Any language with first class functions can be used as a functional programming language.
Any language that meets the above condition and also satisfies the condition that every expression returns a value *is* a functional programming language.
I've found Symbol#to_proc (newly in Rails) quite convenient. I made my own:
so factorial is
(1..n).inject(&:*), and report rewriting is as simple asFile.read('report').map(&:gsub[/java/i,'C#'])I'm new to Ruby but I've very much gotten the impression that Ruby is, as you say, a hybrid functional/OO language. It's part of the reason I'm interested in it in the first place. (Rails is the other big reason.)
One of the things about Ruby which first caught my attention was a Lisper blog saying "Ruby is an acceptable Lisp," which was basically the Lisp community's equivalent of a compliment, and as I understand it the design was inspired partly by Lisp, as well as by Perl, Python, and Smalltalk. In fact if I recall correctly Matz wrote a popular package in emacs Lisp before he wrote Ruby. (I think it was an e-mail client.)
Python is obsessively OO, obviously Smalltalk invented OO, and Perl can be very Lispy at times. So you've got a strong OO heritage and a strong functional heritage. As I said, I'm new to Ruby, but it really does appear that some hybrid elements are definitely in there.
James - we've written a portion of Ruby code that works this way in Mailroom. (SproutIt.com's first app)
We have a series of backend events, i.e. RetrainJunkBackendEvent ProcessMailBackendEvent
They are all subclasses of a 'BackendEvent' method, which defines a base 'run' method.
Each subclass defines its own 'run' method.
The BackendEvents get serialized and de-marshalled at their intended server. (each has a context, defining which server it should get run on)
But.. this is all old hat to you =)
Ruby is pretty powerful stuff, eh!
The idea of passsing blocks as arguments in an OO context isn't new. Smalltalk is OO (obviously) and passes blocks too. This is what makes the Smalltalk collection classes so neat.
Ruby has picked up on this idea aswell.
Back to functional versus OO. I'm not sure if the original OO researchers at Xerox-Parc saw subclassing as the best/only way to get varied behaviour in an OO program. IMO we have just started to think of OO in this limited way because most of us have come to OO through C++ (which doesn't have blocks). Message sends with blocks is as OO in my view as any other type of message send.
BTW. isn't it the lack of side effects and the fact that you can deduce the correctness of a function as a result the key difference between functional and imperative programming?
BTW. In my haste to add my 2 cents, I forgot to say what agreat series of articles these are. I'm sorely tempted to go out and by the book and learn Perl just to follow along.
Great Stuff!
Simula predates Smalltalk. Smalltalk added a lot of innovation, but it's not fair to say it invented OO.
"MJD does talk about how recursion can always be unrolled into an iterative solution."
Well, he shouldn't. Because it's not true. For example, there is no iterative version of the following.
It may look innocuous, but this is an extemely nasty function.
Morton I mean this in the nicest possible way, but you're dead wrong. ;)
Recursion is just using the call stack to manage your parameters. You can always hand-roll your own call stack. Here's the iterative rewrite of your Ackermann function:
I won't pretend that's pretty, but it's certainly doable. It's also faster and less likely to overrun the call stack in Ruby.
A comment on Jason Watkins' comment.
It's a popular mis-conception that Smalltalk took OO from Simula. Alan Kay cites Simula as one of his influences on the early design of Smalltalk, but it was the original Simula (now known as Simula I), and not Simula-67 which adapted Tony Hoare's record class idea, but wasn't itself object-oriented.
Kay defined the term object-oriented as a computation model based on objects responding to messages. Classes were introduced several iterations into the evolution of Smalltalk, and as a way to share behavior, rather than a way to compose abstract data types.
Peter Wegner did a diservice when he took it upon himself to re-define object-oriented as "objects+classes+inheritance" thereby stealing Kay's term and giving it to the Abstract Data Type/Strong Typing clan.
There's a little bit more about this on my blog
Rick: interesting comment. It doesn't match the other references I've seen, which state that the first implementation of smalltalk was done in a couple days in 71, as an attempt at duplicating simula message passing in a one page implementation.
As I recall even the early version of Simula had message passing between coroutines which strongly resembles a concurrent OO language.
Paul is right about side effects. The lack of side effects is essential in a "true" functional programming language. Functional languages like SML have imperative features, but these are always considered to be "not functional" (dysfunctional??)and their usage is generally discouraged.
The fact that you can deduce the correctness is a consequence of the functional paradigm, and not a requirement.