-
-
Save dchelimsky/6808471 to your computer and use it in GitHub Desktop.
require 'wrong/adapters/rspec' | |
# (apply comp fns) in clojure | |
module Composer | |
def compose((*rest, last)) | |
last ||= ->(x){x} | |
rest_reversed = rest.reverse_each | |
lambda do |*args| | |
rest_reversed.reduce(last[*args]) {|result, fn| fn[result]} | |
end | |
end | |
module_function :compose | |
end | |
describe Composer do | |
describe ".compose" do | |
it "returns identity with no fns" do | |
empty = Composer.compose([]) | |
assert { empty.call(3) == 3 } | |
end | |
it "composes with 1 fns" do | |
double = ->(x) { x + x } | |
composed_double = Composer.compose([double]) | |
assert { composed_double.call(3) == 6 } | |
end | |
it "composes with 2 fns" do | |
double = ->(x) { x + x } | |
square = ->(x) { x * x } | |
square_double = Composer.compose([square, double]) | |
assert { square_double.call(3) == 36 } | |
end | |
it "composes with 3 fns" do | |
double = ->(x) { x * 2 } | |
triple = ->(x) { x * 3 } | |
square = ->(x) { x * x } | |
square_double_triple = Composer.compose([square, double, triple]) | |
assert { square_double_triple.call(3) == 18*18 } | |
end | |
it "composes with no-arg fn at end" do | |
double = ->(x) { x * 2 } | |
triple = ->(x) { x * 3 } | |
square = ->(x) { x * x } | |
three = -> (){3} | |
square_double_triple = Composer.compose([square, double, triple, three]) | |
assert { square_double_triple.call == 18*18 } | |
end | |
end | |
end |
(Although it would be better to precompute rest.reverse
before returning the lambda
.)
Oh, and @threedaymonk wisely suggests achieving an rfold with reverse_each.inject
instead of reverse.inject
, which is better all round.
I didn't know about Facets. Facets contains a compose
method on Proc
, but nothing that works on a collection of procs like the clojure one does. I've refactored this code us use the Facets Proce#compose
. It's simpler but it would still be nice to see it on an Array
I think.
@tomstuart: updated with suggestions from you and @threedaymonk.
@mattwynne WDYT?
@dchelimsky Your changes don’t seem to incorporate @threedaymonk’s suggestion of using #reverse_each
, which is preferable to #reverse
since it doesn’t involve allocating a new array.
@tomstuart right you are - fixed
This is very elegant! If you wanted to avoid allocating all those procs at composition time, and doing all that splatting/unsplatting at call time, an alternative is to explicitly treat the last proc as variadic and just rfold
#call
across the rest: