15 minute hack: Poor man’s function composition
June 14th, 2007I 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?











