Higher Order Messaging in Ruby

Having played around with Ruby for some six months now, I feel confident enough to make my first post on the subject. It’s going be about Higher Order Messaging, and it’s more of an expansion on an excellent description by Nat Pryce. I’m writing this post partly because I feel my implementation is a tad better (less cumbersome, as one need not define a class for each new method), and partly because it feels like his post has gone largely unnoticed.

First of all, let’s talk about blocks. Anyone familiar with Ruby will have some knowledge of blocks. Blocks in Ruby is a way to pass a chunk of code as argument to a function; blocks are also full closures, so they may reference any variables that exist in the scope in which they were declared. An example of block usage:

[0,1,2,3,4].select { |val| val.nonzero? }
=> [1,2,3,4]

As you may know, Enumerable#select loops through a collection and returns a new collection containing those elements for which the block evaluates to true. So, the example above produces the array [1,2,3,4], since Numeric#nonzero? will return false for the first element in the array and true for the others.

I expect this is nothing new for most people. Often however, as in the above example, this syntax seems a little verbose.

Using Symbols

You may have come across a little method in the Ruby Extensions project named Symbol#to_proc. In fact, it seems like this hack soon will be an official part of Ruby. Now to achieve the same results as the example above, we can use,

[0,1,2,3,4].select(&:nonzero?)
=> [1,2,3,4]

which is, if nothing else, more compact. Don’t get me wrong, I love this hack. But some object that this syntax is a bit ugly, and I agree partly, so we want to do this some other way, if possible. By understanding how calling methods works in Ruby, we can construct an alternate syntax.

Using Higher Order Messaging

Higher order messaging consists of the idea that method calls (messages), can be passed as arguments. I’ll spoil the surprise and show right away how our new syntax will look:

[0,1,2,3,4].where.nonzero?
=> [1,2,3,4]

Looks good, doesn’t it? It looks like the method where takes a method call (nonzero?) as its argument, forwards it to each element and collects those for which it returns true. That is kind of what happens, but in a much more complicated way. Most of the magic lies in the where method; it returns an object that responds to nonzero?. The object that is returned from where is an instance of HigherOrderMessage:

class HigherOrderMessage
  instance_methods.each { |method| undef_method(method) unless method =~ /__(.+)__/ }

  def initialize(&proc)
    @proc = proc
  end

  def method_missing(id, *args)
    @proc.call(id, *args)
  end
end

It might look a bit confusing, but it’s actually very simple. First note that all functions that aren’t vital (such as __send__) are unlinked, so they may not be called. Second, when constructing a HigherOrderMessage, a block must be supplied, which is stored in the object. Finally, method_missing is defined. This method is a bit special, because it’s called whenever another method doesn’t exist. The first parameter is the name of the method that was attempted; the second parameter is its arguments.

So far, this code does nothing. It’s a mere skeleton to build some functionality from. This is done by in the implemention of Enumerable#where:

module Enumerable
  def where
    HigherOrderMessage.new do |id, *args|
      select { |x| x.__send__(id, *args) }
    end
  end
end

[0,1,2,3,4].where.nonzero?
=> [1,2,3,4]

Let’s dissect the example above; first where is called. What happens is that a new HigherOrderMessage object is created and returned (for brevity, this object will henceforth be referred to as hom). Furthermore, this object has one instance variable—@proc—that contains the code block that was passed to it.

Then, we’re trying to call hom.nonzero?. Since this method doesn’t exist, hom.method_missing(:nonzero?) is called instead. method_missing forwards the method id and its parameters to the block that is stored in @proc. This block consists of a call to select, which in our example evaluates to { |x| x.__send__(:nonzero?) }, equivalent to { |x| x.nonzero? }. Finally, the array that’s generated is returned.

Now you may realize why all defaults methods (those inherited from Object) were undefined: it’s simply so that all methods are forwarded to method_missing. Otherwise, some_array.where.frozen?, for example, wouldn’t work, because it would check whether the HigherOrderMessage object is frozen or not.

Some Other Methods

Let’s construct some other useful methods: do_each, do_collect and do_inject.

module Enumerable
 def do_each
    HigherOrderMessage.new do |id, *args|
      each { |x| x.__send__(id, *args) }
    end
  end

  def do_collect
    HigherOrderMessage.new do |id, *args|
      collect { |x| x.__send__(id, *args) }
    end
  end

  # Requires Enumerable#inject
  def do_inject(start_value)
    HigherOrderMessage.new do |id, *args|
      inject(start_value) { |a,x| a.__send__(id, x, *args) }
    end
  end
end

a, b = [0,1], [1,2]
[a,b].do_each << 3
[a,b]
=> [[0, 1, 3], [1, 2, 3]]
[0,1,2,3,4].do_inject(0).+
=> 10
[0,1,2,3,4].do_collect * 2
=> [0,2,4,6,8] 

The first line adds a new element, 3, to the array. The second like computes and returns the sum of the given array: 10. The third maps the message *(2) to all elements in the array. All examples demonstrate something that would have been impossible to do with Enumerable#to_symbol: when passing a method call you pass both the method name and its arguments, but when you pass a symbol that’s converted to a proc you can’t pass any arguments. Thus, you could never do [a,b].each(&:<<(3)).

Of course, even more complex stuff can be done. Here’s an example of a method that, seemingly, takes two methods as parameters.

module Enumerable
  def having
    HigherOrderMessage.new do |id, *args|
      HigherOrderMessage.new do |secid, *secargs|
        select { |x| x.__send__(id, *args).__send__(secid, *secargs) }
      end
    end
  end
end

[0,1,2,3,4].having.succ < 3
=> [0,1]

The having method, apart from being very useful, demonstrates something interesting about higher order messaging: it can be chained. Not only does having return a HigherOrderMessage instance, even the block in @proc returns one! That means that [0,1,2,3,4].having.succ returns a HigherOrderMessage object that takes yet another function (in our case, <), applies succ to each element and checks it against <(3). Note that this is different than [0,1,2,3,4].succ.where < 3, which would return an array of successors rather than objects from the original array.

A warning: while operators like <, >, ==, and so on may be used in these functions, != mustn’t. From my understanding this is due to a != b being a short form of !(a == b). (E.g., [0,1,2,3,4].succ.where != 3 would expand to !([0,1,2,3,4].do_collect.succ.where == 3), which is false.

Final Words

This is not meant as a replacement for blocks in collection manipulation, but maybe a replacement for Symbol#to_proc. Higher order messaging has, in my opinion, the following advantages:

  • The syntax less wordy than using a block.
  • It’s more flexible than using Symbol#to_proc since you’re effectively forwarding a method call rather than just a method.

It’s not without its problems though. First of all, I’d like to see a version where we can drop the “do_” part of the methods that already exist in Enumerable, and instead have a general function that can either take a block and work like normal, or not take one and behave like a higher order message. And secondly, the implementation itself: right now, this is more of a hack than anything else. It’s probably slower than using a block. And it’s not really tested. At all.

Source code.

41 Responses to “Higher Order Messaging in Ruby”

  1. Bartek Says:

    Sir, this post has left me humbled and afraid. And a little peckish.

  2. Daniel Berger Says:

    Here’s an idea - let’s just modify the methods in Enumerable to accept arguments instead of adding extra syntax that does nothing more than shift the block to the left.

    - Dan

  3. Mike Zillion Says:

    This is so lovely. I’ve never much liked Ruby’s block messaging syntax, despite being impressed by its power. I am very anxious to see how the community responds as this approach matures.

  4. Mike Meng Says:

    Thanks! You teach me a lot.

    Inspired by your work, I think it may be more natrual to keep ’select’ as the HOM name.

    class Array
    alias :old_select :select
    def select(&p)
    if block_given? # conventional call, followed by a block
    old_select(&p)
    else
    HigherOrderMessage.new do |id, *args|
    old_select { |x| x.__send__(id, *args)
    end
    end
    end

    So that we can say:
    (1..100).to_a.select.odd?

  5. Mike Meng Says:

    HTML support?

    class Array
    alias :old_select :select
    def select(&p)
    if block_given? # conventional call, followed by a block
    old_select(&p)
    else
    HigherOrderMessage.new do |id, *args|
    old_select { |x| x.__send__(id, *args)
    end
    end
    end

  6. Istarius Says:

    Thanks for the comments.

    Daniel Berger:

    I see what you mean. While the methods I wrote here were more meant to demonstrate what HOM is and what it can do, it’s true that blocks fills the same functionality and more. It’s still an interesting design pattern, if you ask me. So to clarify: using these specific methods in code for a real project is not something I advise, but learning what HOM is, that can be useful.

    Mike Meng:

    It might be a good idea; however, I’ve got something against overriding methods. So for this example I decided to make new methods instead. Oh, and by the way, I’m pretty much running default Wordpress, so markup in comments are a bit lacking (in fact, I don’t think any kind of markup is allowed). Sorry about that.

  7. Mike Meng Says:

    Thanks. I agree your consideration. Since do_each, do_collect, do_inject and (if ‘do_select’ is used instead of ‘where’) do_select share almost same code, I suggest :

    class Array
    instance_methods.select {|m| [:select, :each, :collect, :inject].include? m.to_sym}.each {|m|
    module_eval

  8. Mike Meng Says:

    Hi Istarius, I’m not complaining, but your comment template system is REALLY broken!

    Try paste my code again:
    class Array
    instance_methods.select {|meth| [:select, :each, :collect, :inject].include? meth.to_sym}.each do |meth|
    module_eval

  9. Mike Meng Says:

    Faint, I give up.

  10. Istarius Says:

    It’s the default system, I guess I should try to find something else that works. And that’s only one of the little quirks I dislike about WP, I might be looking to move on to something else soon…

    Anyway, I see what you’re getting at (I believe), and that’s an excellent idea.

  11. Dopey Says:

    Check out Dr. Nic’s recent work on map_by_method.

  12. Nat Says:

    Cool! That’s a really nice redesign, much better than the way I wrote it originally.

    Do you want to contribute that into the Homer project on RubyForge?

  13. 17 Says:

    Cheap 17…

    Site 17…

  14. 4 Says:

    Read 4…

    You 4…

  15. Ftop Says:

    About ftop…

    Site ftop…

  16. Italy Says:

    My italy…

    Read italy…

  17. Topnado Says:

    Blog topnado…

    My topnado…

  18. Top Says:

    Popular top…

    Cheap top…

  19. Alexvtgoi Says:

    Hello, my name is Alex, i’m a newbie here. I really do like your resource and really interested in things you discuss here, also would like to enter your community, hope it is possible:-) Cya around, best regards, Alex!

  20. BlackBox : My Groovy HOM Says:

    […] A friend of mine sent me this link a while back on Higher Order Messaging in Ruby, which tracks back to this original post on HOM in Ruby and eventually tracks back to a presentation given on HOM in Objective-C. […]

  21. ivagyn Says:

    Hi My Name Is ivatnn.

  22. fajna piosenka Says:

    ABC24…

    chuj w dupe policji…

  23. lijxwobtbz Says:

    Hello! Good Site! Thanks you! zmhfuhyztiajew

  24. suzuki chicago Says:

    suzuki chicago…

    ka-ka-sh-ka 4036326 Value information about suzuki chicago…

  25. interface ipod renault Says:

    2007 jeep tj…

    mazda miata roll bar…

  26. tabkaeevtf Says:

    wrizk

  27. yhfnnvbyym Says:

    cvqvoi

  28. chat latin yahoo Says:

    chat latin yahoo…

    ka-ka-sh-ka 4036326 Advantages of chat latin yahoo….

  29. mature ebony nude Says:

    mature ebony nude…

    ka-ka-sh-ka 4036326 Aggregator of mature ebony nude sites…

  30. nn amateur teen Says:

    nn amateur teen…

    ka-ka-sh-ka 4036326 nn amateur teen moves…

  31. Free XXX Video Says:

    Porno Tie…

    Free Newest XXX Video…

  32. nude asian porn Says:

    nude asian porn…

    Features of nude asian porn….

  33. free nude indian models Says:

    free nude indian models…

    Fresh news on free nude indian models….

  34. Web 2.0 Announcer Says:

    Higher Order Messaging in Ruby…

    […]Higher order messaging is a pattern that allows a more object-oriented approach of manipulating objects, by accepting a method call as a parameter to another method. A rough version of this can easily be implemented in Ruby.[…]…

  35. Zmajrfc Says:

    http://3.kikysexx.info x

  36. cya Says:

    grassrootsyouth…

    grassrootsyouth…

  37. Timati Says:

    http://www.clubsguide.com.au/forum/viewtopic.php?t=1967

  38. car insurance esure Says:

    esure car insurance car insurance esure

  39. Jenifer Marks Says:

    hi
    enj90i3ljsv42yoa
    good luck

  40. Leann Holder Says:

    hi
    enj90i3ljsv42yoa
    good luck

  41. yamaha service manual Says:

    Interesting article, i have bookmarked your site for future referrence :)

Leave a Reply