ruby ruminations – composing Procs

last week i had some time to play around with ruby again. in order to get deeper into it, i startet to study some foreign ruby code. amongst others, i found some very interesting libraries called ruby facets. having a look at some of their implementations caused some headache at first, but on the other side gave some motivation to understand what’s happening and why it’s happening in a certain way. risking a second look and having some ruminations about that code maybe gets me just a little step nearer to the white side of the force …

this blog entry is the first one in a series of so called ruby ruminiations, where i will pick some simple looking ruby code, asking what’s going on there.
i will start with a pretty small extension of class Proc, which i found in the above mentioned ruby facets core library.

now here is that little darling, asking me for some extra time to stay with’em:

class Proc
    def *(g)
        raise ArgumentError, "arity count mismatch" unless arity == g.arity
        proc{ |*a| self[*g[*a] ] }
    end
end

but from the very first …
as you can see, we extend the build-in class Proc, since all of ruby’s classes are open for that kind of extension.

Procs

Procs are – simply said – some block of code, that have been bound to a set of local variables (see the pickaxe book). This bunch of code is captured and represented by a Proc object that can be called at any time after it’s definition:

double = Proc.new {|x| x * 2 }
double.call( 5 ) # => 10

alternatively you can use the so called lambda operator, which will instantiate a given code block as a Proc object:

half_of = lambda {|x| x / 2 }
half_of.call( 8 ) # => 4

and – not enough – another way to define a proc is using Kernel#proc:

square = proc {|x|x*x}
square( 4 ) # => 16

 

i spy with my little eye something beginning with …

as seen, Procs can take an arbitrary number of arguments (‘x’ as the only argument in the above examples), delivering a return value which is by default evaluated as the last expression inside that code block – so a Proc looks like an anonymous method, which can be assigned to a variable.

But more than a method, a Proc is able to refer to the set of local variables bound to the context it was defined within (even if that context isn’t visible anymore to the callers environment):

def salutation( male ,female )
    female_names = [:Alicia, :Samantha, :Kathy] 
    male_names = [:John,:Ben,:Harvey]

    lambda do |name|
        return female + name if female_names.include? name.intern
        return male + name if male_names.include? name.intern
        "Dear " + name
    end
end

salutation_friends = salutation("Yo ","Hi ")
salution_pen_pals = salutation("Howdy ","Hello ")

puts salutation_friends.call("Ben")
# => 'Yo Ben'

puts salution_pen_pals.call("Alicia")
# => 'Hello Alicia'

puts salution_pen_pals.call("Dave")
# => 'Dear Dave'

first of all, the two arrays defining the known male and female names are (only) visible inside the scope of method salutation(). but even after the method execution ends, the created Proc refers to that arrays when called afterwards. this is also true for the given arguments with which the method salutation() gets called. even if we invoke the method more than once with different arguments, the created Proc ‘remembers’ those different values!
furthermore, you surely have noticed that we used the do…end syntax for defining the code block. this is a very suitable way when having a code block with more than one line.

it’s not a bird

we can use another notation in order to invoke a Proc object. up to now, we called Proc’s instance method call() for invocation. as a shortcut you can always use []. when i saw this notation the first time, i was bit of confused – this seems a little odd since it may remind you of the notion of an array. of course ruby lets you override the [] operator in any class you want to, and so it is for class Proc as a synonym for Proc#call.

puts salutation_friends["Kathy"]
# => 'Hi Kathy'

whatever way you’ll choose to invoke a Proc object, you may want to know the correct number of arguments a Proc object can handle. in this case you can use the instance method Proc#arity on the object. it will betray the number of arguments, which will be processed by the Proc’s instance.
in case you have a Proc object that accepts an argument array (we will have a deeper look at this kind of argument in the next section) Proc#arity will return a negative number – begining with -1 for only having an argument array, decreasing the number with every additional mentioned single argument:

lambda {|arg1|}.arity
# => 1

lambda {|arg1,arg2,arg3|}.arity
# => 3

lambda {|*args|}.arity
# => -1

lambda {|arg1,*args|}.arity
# => -2

lambda {|arg1,arg2,arg3,arg4,*args|}.arity
# => -5

open-hearted

Procs are very communicative! as mentioned before, you can pass an arbitrary number of arguments to a Proc object. it’s also possible to use the array notation like in method definitions in order to access a dynamic number of arguments:

pick = lambda {|pos,*cards| cards[pos-1] if pos > 0 and pos < cards.size}

pick[3, 'diamonds', 'hearts', 'spades', 'clubs']
# => 'spades'

you have to be careful when delegating an argument array to a Proc object, for example inside a method. if you forget to use the asterisk again when calling the Proc with the given argument array, the Proc will only receive a single, ‘joined’ argument:

def start_play(pick,*cards)
    # here we delegate the argument array correctly
    pick[1, *cards] 
end

def next_move( pick, *cards )
    # oops, we missed the asterisk !
    pick[2, cards]
end

first_card=start_play (pick,'diamonds','hearts','spades','clubs')
# => 'diamonds'

second_card=next_move (pick,'diamonds','hearts','spades','clubs' )
# => nil

the same is true for return value(s). since you are able to return more than one value in ruby, you could return an array of return values:

words_with_length = lambda do|c,*words|
    hits = []
    words.each do |word|
        hits << word if word.size == c
    end
    hits
end

words_with_length [4,"Sue","Mike","Walter","Tara","Beth","David" ]
# => ['Mike', 'Tara', 'Beth']

again, if you want to pass an result array of a Proc as an argument to another Proc which can handle an argument array (sounds as if we could nest calls of Proc objects, eh?), again you have to watch out not to forget the asterisk! otherwise your result array will be also handed over as a single, joined argument:

line_print = lambda do |*words|
    words.each_index do |i|
        puts "#{i+1} #{words[i]}"
    end
end

# ok, asterisk on board !
line_print[ *words_with_length [4,"Sue","Mike","Walter","Tara","Beth","David"]]

1 Mike
2 Tara
3 Beth

# ouch, missed again:
line_print[ words_with_length [4,"Sue","Mike","Walter","Tara","Beth","David"]]

1 MikeTaraBeth

inside out

now we have enough information to get back to our ‘darling from page 1’ … here’s the code again:

class Proc
    def *(g)
        raise ArgumentError, "arity count mismatch" unless arity == g.arity
        proc{ |*a| self[*g[*a] ] }
    end
end

now it’s easy to understand what’s happening inside the method Proc#*. we will go through the code from inside out:
first of all, we can see, that the method will create and return another Proc object (using Kernel#proc for instaniating the Proc object), which will accept a dynamic range of arguments.

this argument array is passed to the proc named g. it’s the argument of Proc#*(as the newly created and returned Proc object is able to refer to all variables of its defining context, it of course can refer to Prog g when it will be called).

we know furthermore, that we have to keep the asterisk when passing the argument array to a Proc instance, thus also when passing it to Proc g, using the []-operator notation (‘g[*a]‘).

So what happens next? at the end of Proc g’s execution it may will return one return value or even a return value array. since we want to be generic, we allow an arbitrary number of return values (which subsumes all Procs which will return one or no return value at all).

the call of Proc g is nested inside the call of the current Proc object (which is represented by ‘self’). so the argument of self Proc is the return of Proc g (never fear, we’ve already seen nested calls before and this is pretty the same thing).
since Proc g returns at least an arbitrary number of arguments which shall be passed as an argument array to the self Proc as the receiver, again we have to keep the asterisk (‘self[*g[*a]’).

thats all of the relevant stuff. before creating this new Proc, which is nesting the calls of self Proc and the Proc given as the argument, there is a check, if the number of arguments of these two Procs are the same. otherwise an exception is raised. from my point of view this check acts only as a kind of makeshift, since we should check if the number of return values of the inner Proc meets the number of arguments the outer Proc is willing to accept.

we now could use our new notation of composing two procs:

square = lambda {|x| x*x}
cube_length = lambda{|volume|volume**(1.0/3)}

cube_area = square * cube_length

cube_area[100]
# => 21.5443469003188

this is a pretty small example, but it illustrates the idea very well.
composing Proc objects can now be done in a simple, concise way. since ruby doesn’t demand dot notation for method calls (the asterisk between the two Procs is automatically assumed to be a method of the object that stands on the left side of the method’s identifier) we have gained a nice and readable way of Proc composition.

but beside that, we may have got a little deeper insight of how Procs do their job … ;o)

Advertisements
Posted in ruby. 5 Comments »

5 Responses to “ruby ruminations – composing Procs”

  1. Kyle Brooks Says:

    Great post!

  2. chris Says:

    great writeup, thx!

  3. Fouad Mardini Says:

    a very enjoyable read. Thanks!

  4. joe Says:

    Decent content, but would it kill you to capitalize?

  5. nicolas Says:

    very nice read, quite clear, it put things back in order.
    facets is a place with lots of gems.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: