15 minute hack: Poor man’s function composition

June 14th, 2007

I loves me some functional programming and I loves me some Symbol#to_proc (which lets you do .each(&:upcase)).

One of those _fp_ features I wish I had in ruby-land was function composition. Specifically, I find myself writing things like:

initials = ["Foo". "Bar", "Baz"].map{|str| str.first.upcase}

That is fine, but just isn’t as cool as leveraging to_proc in some fashion. In the more “stupid, stupid, stupid” case, i’ve also seen things like:

initials = ["Foo". "Bar", "Baz"].map(&:first).map(&:upcase)  # Eww, unnecessary iterations...

Anways, Ruby does have function composition ([Proc#compose][2]), thanks to the Ruby facets project, but working directly with lambdas isn’t nearly as friendly as I desire. So, I spent 15 minutes to hack together a means that I like. Here’s what it looks like:

["foo", "bar", "baz"].map(&:first >> :upcase)

Which reads, map, using :first then :upcase. >> is left associative, like "foo".first.upcase, opposite of Haskell composition where foobar = foo . bar reads foo follows bar.

Also, the snippet linked below lets you do something like:

  class String
    compose(:initial, &:first >> :upcase)
  end
  "foo".initial   # => "F"

Now a re-implementation of ActiveSupport’s Module#delegate:

class Module
  def delegate(*methods)
    options = methods.pop
    unless options.is_a?(Hash) && to = options[:to]
      raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
    end

    methods.each do |method|
      compose(method, &to >> method)
    end
  end
end

Perhaps not necessary, but I found it interesting and fun to write. Thoughts?

compose.rb

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • DZone
  • Digg
  • Reddit
  • del.icio.us
  • Blue Dot
  • StumbleUpon
  • Netscape
  • Ma.gnolia
  • Slashdot
  • Technorati
  • NewsVine
  • Netvouz

Leave a Reply