The Ruby Murray Cookbook - Recipes for Ruby’s Sub::Curry port.
This is a translation of the Sub::Curry cookbook to Ruby Murray. See the original at:
©2006 Ross Bamford. Same license as Ruby.
In case you’re wondering, see en.wikipedia.org/wiki/Ruby_Murray.
Right currying
Special spices make rcurry unnecessary. You can add trailing spice using blackholes.
c = lambda { |a,b,c| (a + b) / c }.curry(Curry::BLACKHOLE, 2)
c.call(5,3)
# => 4
Convert func -> method / method -> func / Curry a method
These are not really applicable to Ruby - the original was working around Perl’s broken implementation. But I guess something like:
def send_func(obj, msg, *args)
obj.send(msg,*args)
end
c = method(:send_func).curry("object")
c.call(:length)
# => 6
c.call(:+, "ive C")
# => "objective C"
And (more useful?):
c = "string".method(:slice).curry(Curry::HOLE,2) c.call(0) # => "st" c.call(2) # => "ri"
Add trailing spice after a hole in a curried proc
You have a hole in your curried subroutine but want to add more spices after or fill out holes after your first hole
Assuming no special spice before the hole, just put a hole in the hole.
plus = method(:send_func).curry(Curry::HOLE,:+)
"#{plus.spice.inspect} -> #{plus.call("object","ive")}"
# => "[<HOLE>, :+] -> objective"
plusion = plus.new(Curry::HOLE, "ion")
"#{plusion.spice.inspect} -> #{plusion.call("object")}"
# => "[<HOLE>, :+, "ion"] -> objection"
Add trailing spice after a blackhole
You want to add more spice, but have a blackhole that swallows anything you try to put in there. You want to keep the blackhole, though.
withtwo = method(:send_func).curry(Curry::BLACKHOLE,2)
"#{withtwo.spice.inspect} -> #{withtwo.call([1,2,3,4],:index).inspect}"
# => "[<BLACKHOLE>, 2] -> 1"
Using a blackhole follwed by a whitehole, we can add extra spice at the end:
twooh = withtwo.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 0)
"#{twooh.spice.inspect} -> #{twooh.call([1,2,3,4],:[]=).inspect}"
# => "[<BLACKHOLE>, 2, 0] -> 0"
Or by placing spice between the black and white holes we can place it immediately after the original blackhole.
ohtwo = withtwo.new(Curry::BLACKHOLE, 0, Curry::WHITEHOLE)
"#{ohtwo.spice.inspect} -> #{ohtwo.call([1,2,3,4],:slice).inspect}"
# => "[<BLACKHOLE>, 0, 2] -> [1, 2]"
By giving the blackhole another blackhole, you have two successive blackholes, the first newly added and the second the one that added the new. The whitehole is then removes the second blackhole — the blackhole doing the inserting. The spice is then added like usual, yet a blackhole exists at the right place.
Append raw spice without processing
You have a curry that may have special spices in it. You want to add more spices. You do not want the new spice to be processed as an application on the old spice. You just want to append more spice.
To do this, you’ll have to manually get the parts you need and make a new curry, since applying the new spice the usual way results in it being processed.
sary = lambda { |*args| args }.curry(Curry::HOLE,'Array')
"#{sary.spice.inspect} -> #{sary.call('The',2,3).inspect}"
# => "[<HOLE>, "Array"] -> ["The", "Array", 2, 3]"
sary2 = Curry.new(*sary.spice + [Curry::BLACKHOLE, 'The end'], &sary.uncurried)
"#{sary2.spice.inspect} -> #{sary2.call('The',2,3).inspect}"
# => "[<HOLE>, "Array", <BLACKHOLE>, "The end"] -> ["The", "Array", 2, 3, "The end"]"
Pass curries as blocks to methods
Curry implements the to_proc protocol so you’re away.
adjsum = lambda { |sum, val, adj| sum + (val * adj) }.curry(Curry::BLACKHOLE, 2)
[1,2,3,4,5].inject(0,&adjsum)
# => 30
Apply block arguments via spice
Right now, Curry doesn’t treat block arguments in the spice specially, and in particular won’t unwrap (with &) a block at the end of your spice. Also, since it’s not possible to pass a block to another block, you cannot pass blocks to your curried procs or chain them together.
You can pass blocks to curried methods in a call, but only when they’re created from bound methods (passed before the spice, or from Method#curry).
curry = [10,20,30].method(:inject).curry(Curry::HOLE)
curry.call(0) { |s,i| s + i }
# => 60
Taking this a step further, you can do some pretty mad stuff:
mult_sum = lambda { |sum, i, mult| sum + (i * mult) }.curry(Curry::BLACKHOLE, Curry::HOLE)
double_sum = mult_sum.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 2)
triple_sum = mult_sum.new(Curry::BLACKHOLE, Curry::WHITEHOLE, 3)
curry.call(0, &double_sum)
# => 120
curry.call(0, &triple_sum)
# => 180
(Wow, that’s pretty useless ;)) Ruby doesn’t allow block arguments to blocks, so a block attached to a curried proc invocation is ignored.