-
-
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
#callacross the rest: