Lab42::Curry
Name says it all..
Curry functions and methods at will, reorder, define placeholders anywhere, positional and named args
N.B. All these code examples are verified with the speculate_about gem
So what does it do?
Context Positional Parameters
The simplest and classical way to curry a function (I include methods when I say function) is by providing the first
n
parameters to a function needing m
parameters and thusly defining a function that needs now m - n
parameters.
Given such a simple funcion
def adder(a, b, c); a + 10*b + 100*c end
let(:add_to_1) {curry(:adder, 1)}
# Equivalent to Elixir's &adder(1, &1, &2)
N.B. that Lab42::Curry
has been included into Examples and ExampleGroups in spec/spec_helper.rb
Then very unsurprisingly:
expect(add_to_1.(2, 3)).to eq(321)
We call the arguments passed into curry
the compiletime arguments, and the arguments passed into the
invocation of the curried function, which has been returned by the invocation of curry
, the runtime arguments.
In our case the compiletime arguments were [1]
and the runtime arguments were [2, 3]
Reordering
There are several methods of reordering arguments, the simplest is probably using placeholders.
When a placeholder is provided (Lab42::Curry.runtime_arg
aliased as rt_arg
)
let(:add_to_30) { curry(:adder, rt_arg, 3) }
# Equivalent to Elixir's &adder(&1, 3, &2)
Then we see that
expect( add_to_30.(1, 5) ).to eq(531)
Total control over argument order...
... can be achieved by passing the index of the positional argument to be used into Lab42::Curry.runtime_arg
Given the total reorder form
let(:twohundred_three) { curry(:adder, runtime_arg(2), runtime_arg(0), 1) }
# now first argument is c (index 2) and second a (index 0) and b = 1
# Like Elixir's &adder(&2, 1, &1)
Then we have
expect( twohundred_three.(2, 3) ).to eq(213)
Picking a position for a compiletime argument
It might be cumbersome to write things like: curry(..., rt_arg, rt_arg, ..., rt_arg, 42)
Therefore we can express the same much more concisely with Lab42::Curry.compiletime_args
, and its alias ct_args
Given
let(:twohundred) { curry(:adder, ct_args(2 => 2)) }
# same as curry(:adder, rt_arg, rt_arg, 2)
Then we get
expect( twohundred.(4, 3) ).to eq(234)
N.B. that we could have defined add_to_30
as curry(:adder, rt_arg, 3, rt_arg)
of course
Error Handling
When you indicate values for the same position multiple times
Then the ArgumentCompiler
saves you:
expect{ curry(:adder, 1, ct_args(0 => 1)) }.to raise_error(Lab42::Curry::DuplicatePositionSpecification)
Context With proc like objects
Given a lambda
let(:sub) { ->{ _1 - _2} }
let(:inverse) { curry(sub, rt_arg(1), rt_arg) }
Then we will get the negative value
expect( inverse.(2, 1) ).to eq(-1)
Context Keyword Arguments
Given a function which takes keyword arguments like the following
def rectangle(length, width, border: 0, color: )
[length, width, border, color]
end
let(:red_rectangle) { curry(:rectangle, color: :red) }
let(:wide_bordered) { curry(:rectangle, rt_arg, 999, border: 1) }
Then the red rectangle gives us
expect( red_rectangle.(1, 2) ).to eq([1, 2, 0, :red])
expect( red_rectangle.(1, 2, border: 1) ).to eq([1, 2, 1, :red])
Can we override curried values, normally not Example: cannot override
expect{ red_rectangle.(1, 2, color: :blue) }
.to raise_error(
Lab42::Curry::DuplicateKeywordArgument,
"keyword argument :color is already defined with value :red cannot override with :blue")
But we can create a more lenient curry with curry!
expect( curry!(:rectangle, 1, 2, color: :red ).(color: :blue) )
.to eq([1, 2, 0, :blue])
LICENSE
Copyright 2020,1 Robert Dober [email protected]
Apache-2.0 c.f LICENSE