block
Functional Programming in Ruby
After many years of software development in the imperative style under my belt, I have in recent years developed a keen interest in functional programming as an alternative paradigm. First via the confusion of Functional Java, then Scala and now some forays into Haskell. Having experienced first hand the improvement in legibility and comprehension the functional style can bring, I naturally wondered how FP could be applied to our Ruby work on Yellow Lab.
Ruby’s strength is in its loose typing and imperative style. I like to think of it as the new and improved Perl. A rapid prototyping and scripting language for the modern developer and technical SME alike. Whilst it lacks the rigour of a purely functional language (as all imperative languages do) it does have functional capabilities.
The essence of the functional style is the application of functions instead of variable mutation. It follows that functional languages must support function expressions for there to be any prospect of applying the consistent behaviour over an enumeration. Ruby supports three types of function expressions:
Blocks
Anyone who has used collect or inject on a map knows what a block is. They are anonymous function expressions which are applied in a method when the yield keyword is encountered.
fruits.each {|fruit| eat fruit } # delicious!
In this example the brackets denote the block, the pipes delimit the input parameter list and the remainder is the block body to be executed. Blocks cannot be assigned to a variable so their reuse is limited. One of the Yellow Lab developers has written more about Ruby blocks on his blog.
Procs
Procs are references to blocks. The are constructed explicitly with Proc.new or implicitly when ampersand prefixed method parameters receive anonymous blocks. They are invoked with a call to call.
def do_something_with_fruit(fruit, &fruit_related_activity)
fruit_related_activity.call(fruit)
end
do_something_with_fruit(banana) {|fruit| eat fruit} # => the banana is eaten!
On first sight they seem fit for purpose. But they have some strong limitations.
1. Flow control statements, such as return and break, will affect the flow in the invoking method, not just in the proc.
2. They can happily be invoked with an incorrect numbers of parameters. Some may think this is a benefit, but I believe it is an open invitation for bugs.
Lambdas
Lambdas are akin to Procs, but correct the two limitations listed above. That is, flow control statements take effect within the scope of the lambda only and the number of parameters must be correct. They are the best fit for reusable references to function expressions.
blend = lambda {|fruit| blender.blend(fruit)}
freeze = lambda {|fruit| freezer << fruit}
basketOfFruit.each &blend # => a smoothie
basketOfFruit.each &freeze # => now icecream
canOfWorms.each &freeze # => um ...
Utility
Our application uses the Rails framework and has many of the artefacts you’d expect to see in a Rails application: controllers, models, views, some rake and capistrano tasks. Much of it is elegant and simple imperative style code. Blocks are used frequently within Labs to simplify the application of behaviour across an enumerable – especially in rake and capistrano tasks that prepare our data for runtime. There seems little necessity in this kind of application for the inclusion of lambdas. (I expect this would be quite a different situation if we were building a framework instead).
Ruby is far from a functional language. No one could possibly argue otherwise. The lack of typing and immutability ensure that it is too tempting to revert to a less rigid style. But, as this post has shown, there are some functional aspects of the language and it is certainly worth keeping these in the toolbox.