Does this make my code look big?

February 15, 2009

Chapter 4 – Ruby

Filed under: Chapter 4, Code, Exercise, Ruby — Barry Allison @ 2:31 pm
Tags:

Chapter 4 solution

The 2 main things of interest tackling chapter 4 in ruby are the use of recursion and the impedance mismatch handling functional arguments.

First, writing recursive methods is less common in ruby than scheme or haskell [citation needed]. I think there are a couple of reasons for that.First is the rich use of blocks in ruby – lots of recursive procedures can be replaced with blocks using an Enumerable object, and there are plenty of those in ruby. Compare the range_product method:

rangeProduct :: Int -> Int -> Int
rangeProduct x y
    | x > y     = error "rangeProduct start is greater than end"
    | x == y    = y
    | otherwise = x * rangeProduct (x+1) y

def range_product(x, y)
  raise "range_product start is greater than end value" if x > y
  (x..y).inject(1) {|prod, i| prod * i }
end

Creating an Enumerable range object and using inject – ruby’s fold equivalent is much more idiomatic than writing a recursive method.

Secondly there is no tail call optimisation in ruby – at least not in 1.8.6. (I think the 1.9 vm may introduce tail call optimisation). This means every recursive call builds up the call stack – which is fine on small levels of recursion but scheme guarantees tail call optimisation and the haskell compiler internally uses such optimisation where it can identify the need – I seem to remember reading that if the last line of code is a recursive call it will be optimised.

The use of functional arguments is pretty painful and inconsistent in ruby compared to both scheme and haskell. For example if I have an id method, I can’t just write 1 + sum_fum(id, n) in the way that is possible in scheme and haskell – which feels natural and intuitive. As far as I can tell there are 3 options:

  • Wrap the method invocation in a new Proc object – Proc.new {|x| id(x) }
  • Wrap the method invocation using a lambda generated Proc ojbject lambda {|x| id(x) }
  • Pass a block into the method invocation and have the method convert the block into a Proc object

The problem with these is they all behave differently Proc and lambda have different semantic behaviour when the invoked method uses a return statement. Using a block means the method being invoked has to generate a Proc object from the block passed in – admittedly that’s easy to do but the problem is having to remember all the subtle differences rather than just being able to use a single consistent mechanism for dealing with functional arguments. Here ruby is definitely lacking, obtuse and obscure.

No Comments Yet »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

You must be logged in to post a comment.

Blog at WordPress.com.