What about being able to shorten users.collect { |user| user.order.city } or its unefficient form users.collect(&:order).collect(&:city) into

{% highlight ruby %} users.collect(&[:order, :city]) {% endhighlight %}

It can be achieved by composing functions, in Ruby’s case by composing Procs. This is an amusing exercise that demonstrates Ruby’s functional abilites.

{% end_excerpt %}

Just in case you need to freshen your memory about composing functions, it’s a common notion in functional programming and it’s also found early in mathematics courses.

Given two functions f(x) and g(x), (g o f)(x) == g(f(x)), o being the symbol of the composition operation.

Unchaining method calls

users.collect { |user| user.order.city }

The first step here is to compact the #order and #city method calls.

To achieve that, it’s interesting to know how methods calls are done under the hood. Python, by being explicit by design as opposed to Ruby, gives a clear answer :

{% highlight python %} class User def init(self, firstname, lastname): self.firstname = firstname self.lastname = lastname

def name(self): return self.firstname + self.lastname {% endhighlight %}

Each method accepts a first argument which is always self. It’s simple as that, a method is nothing more than a function whose first argument is the instance. This way, attributes can be accessed trough self inside the function. Under the hood in Ruby @firstname is basically just a nice way to grab it from the instance without having to be explicit about self.

Back to our example, with that knowledge we can say that :

{% highlight ruby %} users.collect { |user| user.order.city } {% endhighlight %}

is equivalent in the underlying implementation to :

{% highlight ruby %}


users.collect { |user| city(order(user)) } {% endhighlight %}

So that’s it, in theory we got functions here and composing them makes sense :

{% highlight ruby %}

pseudo-code, ‘o’ being the hypothetical composition operator.

get_order_then_city = city o order users.collect { |user| get_order_then_city(user) } {% endhighlight %}

Finally, let’s convert that to real Ruby code, artificially reverting methods to their primitive forms, functions.

{% highlight ruby %} order = Proc.new { |user| user.order } city = Proc.new { |order| order.city } {% endhighlight %}

Those two Procs still need to be composed, sadly Ruby don’t come with a defined composition operator for Proc, so let’s write one.

Composing functions in Ruby

As Procs are Ruby objects, it’s simply a matter of adding a composition operator to the Proc class. As the symbol used in mathematics, o can’t be used here, it’s usually * that takes its place.

{% highlight ruby %} increment = Proc.new { |x| x + 1 } square = Proc.new { |x| x * x }

increment_and_square = square * increment

p increment_and_square(2)

=> (2+1)^2 = 9

{% endhighlight %}

Implementation is pretty straight-forward :

{% highlight ruby %} class Proc def *(other) Proc.new { |x| call(other.call(x)) } end end {% endhighlight %}

Now increment and square can be composed throught the * operator and it works as expected.

Back to business

At this point, the job is almost finished. Procs can be composed, and symbols can be converted to Procs thanks to &:order.

&:order is quite common but before composing it, how does it really works ?

Behind its somewhat exotic syntax, it calls #to_proc which creates a Proc that sends the symbol itself, (:order in this case) to an object. Then it converts the Proc into a block so it can be passed to methods like #each or #collect that expects one.

In more concrete terms &:order creates the following Proc:

{% highlight ruby %} get_order = Proc.new { |user| user.order } {% endhighlight %}

And its generalized form:

{% highlight ruby %} class Symbol def to_proc # This is a simplified version, the real one can handle multiple # arguments. Proc.new { |object| object.send(self) } end end

{% endhighlight %}

Such Procs can as previously seen, be easily composed with the brand new * operator on Procs.

At this point it can be tempting to write users.collect(&:city * &:order) but this can’t work. As a block isn’t an object, calling any method on it (#* in this case) makes absolutely no sense. Only a single unary & can exist in an expression. Ruby will raise a SyntaxError if a second one is present.

The correct syntax with a single & isn’t really shiny, but it works as expected.

{% highlight ruby %} users.collect(&(:city.to_proc * :order.to_proc))

=> [‘Kuala Lumpur’, ‘Paris’]

{% endhighlight %}

But frankly, from a syntactic point of view, it’s sill far from being simpler than a traditional users.collect { |user| user.order.city } and the order feels a bit backward.

Adding Syntactic Sugar

Even if it’s just for fun, better syntax can be achived by calling Array to the rescue. Having a list of Procs that will be composed makes some sense and provides a lighter syntax.

{% highlight ruby %} users.collect(&[:city, :order])

=> [‘Kuala Lumpur’, ‘Paris’]

{% endhighlight %}

#to_proc can be added to basically any object, while this opens many weird and exotic possibilites it suits perfectly what is needed here.

So building a Proc from an array of symbols, given they can be converted to procs and then composed, can be written as the following:

{% highlight ruby %} class array def to_proc collect(&:to_proc).inject(&:*) end end

users.collect(&[:city, :order])

=> [‘kuala lumpur’, ‘paris’]

{% endhighlight %}

Yet without knowing we’re composing stuff under the hood, it would be nice to have the symbols ordered like the chained method calls.

{% highlight ruby %} class array def to_proc reverse.collect(&:to_proc).inject(&:*) end end

users.collect(&[:order, :city])

=> [‘kuala lumpur’, ‘paris’]

{% endhighlight %}

And it does the job and with a nice syntax!

The only bad thing here is it has to create a Proc for each symbol and that’s why nobody should use it in real code. A less fun but more practical version can be written by using #inject and #send:

{% highlight ruby %} class Array def to_proc Proc.new do |object| inject(object) do |this, method_id| this.send(method_id) end end end end {% endhighlight %}