Ruby Murray is a Ruby port of Perl’s Sub::Curry library that allows curried blocks and methods to be handled in a pretty flexible way.

See search.cpan.org/~lodin/Sub-Curry-0.8/lib/Sub/Curry.pm and search.cpan.org/~lodin/Sub-Curry-0.8/lib/Sub/Curry/Cookbook.pod for details on the original, and some general background.

Simple usage:

  curry = lambda { |*args| args }.curry(Curry::HOLE, "foo", Curry::HOLE)
  curry.call(1,3)     # => [1, "foo", 3]

  curry = "string".method(:slice).curry(Curry::HOLE, 2)
  curry.call(0)       # => "st"
  curry.call(2)       # => "ri"

The curry methods are provided by the Curriable module, which simply provides convenient wrapping for Curry.new. There are a few variations between the various forms, but mostly they are equivalent and can be used interchangeably.

See TestCurry (and click the method signatures) for more usage.

Curried procs are immutable once created. If you wish to apply further special spice to a curried method, you may do so either using the instance method new to create a new curried proc by applying new spice to the old spice, or by passing the special spices directly to a call.

You can download Ruby Murray (with documentation and explanatory comments) from roscopeco.co.uk/ruby-quiz-entries/64/curry.rb

Methods
Classes and Modules
Class Curry::LazySpice
Class Curry::SpiceArg
Constants
VERSION = '0.1.2'
WHITEHOLE = Object.new
  A whitehole removes the blackhole, but the spice that has been put into the blackhole remains since blackholes themselves don’t store anything.
ANTIHOLE = Object.new
  An antihole put in a hole makes the hole disappear. If the spice is 1, <HOLE>, 3, <HOLE>, 4 and 2, <ANTIHOLE>, 5 is applied then the result will become 1, 2, 3, 4, 5.
HOLE = HoleArg.instance
  A hole is what it sounds like: a gap in the argument list. Later, when the subroutine is called the holes are filled in. So if the spice is 1, <HOLE>, 3 and then 2, 4 is applied to the curried proc, the resulting argument list is 1, 2, 3, 4.

Holes can be called "scalar inserters" that default to nil.

BLACKHOLE = BlackHoleArg.instance
  A blackhole is like a hole for lists that never gets full. There’s an imaginary untouchable blackhole at the end of the spice. The blackhole thusly inserts the new spice before itself. The blackhole never gets full because nothing is ever stored in a blackhole as it isn’t a hole really…

Blackholes are used to move the point of insertion from the end to somewhere else, so you can curry the end of the argument list.

Blackholes can be called "list inserters" that defaults to the empty list.

ANTISPICE = AntiSpiceArg.instance
  An antispice is like a hole except that when it’s filled it disappears. It’s like a combination of a hole and an antihole. If the spice is 1, <ANTISPICE>, 3 and 2, 4 is applied, then the result will become 1, 3, 4.
Attributes
[R] spice The raw spice held by this curried proc. May contain special spices.
[R] uncurried The block (Proc) for which arguments are curried.
Public Class methods
Curry.new(*spice) { |*args| ... } → #<Curry...>
Curry.new(callable, *spice) → #<Curry...>

Create a new curry with the specified spice and block or callable object. The second form requires only that the first argument respond_to?(:call)

     # File lib/curry.rb, line 172
172:   def initialize(*spice, &block)
173:     block = block || (spice.shift if spice.first.respond_to?(:call))
174:     raise ArgumentError, "No block supplied" unless block
175:     @spice, @uncurried = spice, block
176:   end
Public Instance methods
some_curry.call(*args) { |b| ... } → result
some_curry[*args] { |b| ... } → result

Call the curried proc, passing the supplied arguments. This method resolves all special spices and passes the resolved arguments to the block. If a block is passed to call it will be passed on as a block argument to the curried method only if this curry was created from a Method. Curries created from a block passed to Curry.new (or from Proc#curry) cannot have block arguments passed to them.

Unlike Perl’s Sub::Curry implementation, special spices may be passed in the call arguments, and are applied as with new. This means that whiteholes and antiholes can be passed in to make single-call modifications to the argument spice. This probably isn’t as great on performance but it’s more fun.

see also new

     # File lib/curry.rb, line 197
197:   def call(*args, &blk)
198:     @uncurried.call(*call_spice(args), &blk)
199:   end
some_curry.new(*spice) → #<Curry...>

Create a new curried proc by applying the supplied spice to the current spice in this curried proc. This does not simply append the spices - Arguments in the supplied spice are applied to the curried spice arguments, with black/white hole and antiholes operating as documented.

See also call.

     # File lib/curry.rb, line 216
216:   def new(*spice)
217:     Curry.new(*merge_spice(spice), &@uncurried)
218:   end
some_curry.to_proc → #<Proc...>

Convert to a proc

     # File lib/curry.rb, line 224
224:   def to_proc
225:     # since we're immutable we can keep this
226:     @extern_proc ||= method(:call).to_proc
227:   end