# Creating a lambda
l = lambda { |name| "Hi #{name}!" }
# Executing the lambda
l.call("foo") # => Hi foo!
# Creating a lambda using shorthand notation
l = -> name { puts "Hi #{name}!" }
# Excuting the lambda using shorthand notation
l.("foo") # => Hi foo!
# Multiple arguments
l = -> name, age { puts "Hi #{name}! You're #{age} years young" }
l.("foo", 32) # => Hi foo! You're 32 years young
# No arguments
l = -> { puts "foo!" }
l.() # => "foo!"
Lambda's also enforce a closure and so are able to keep their context across objects, as demonstrated below:
require "json"
class Bar
attr_reader :l
def initialize(h = {})
@l = h[:l] || -> _ { p "no-op"; false }
end
def dothing
result = l.("Mark")
p "result = #{result}"
end
end
class Foo
def initialize
@h = {
:l => -> name { p "hello #{name}"; foo_test }
}
@bar = Bar.new(@h) # remove @h to test for defensive behaviour
end
def start
@bar.dothing
end
private
def foo_test
p "I'm internal to Foo class"
raise ::JSON::ParserError
true # never reached due to above line triggering an error
rescue ::JSON::ParserError
p "caught an error"
false
end
end
foo = Foo.new
foo.start
The default output of the above program is:
"hello Mark"
"I'm internal to Foo class"
"caught an error"
"result = false"
Partial function aplication is calling a function with some number of arguments, in order to get a function back that will take that many less arguments.
Currying is taking a function that takesn
arguments, and splitting it inton
functions that take one argument.
In order to give you a clearer idea of what each of these two things will do a function, let’s take an example Proc:
proc { |x, y, z| x + y + z }
Partial application of this function would return, if we passed in the first two arguments, the following nested Procs:
proc { |x, y| proc { |z| x + y + z} }
On the other hand, currying this function would return the following nested Procs:
proc { |x| proc { |y| proc { |z| x + y + z} } }
Note that you can only pass in one argument at a time to the result of a curried function, but pass as many as you like at a time when using partial application. This is the core principal that defines these two applications. TheProc#curry
method in Ruby allows you to execute both of these applications.
Currying: continuously partially apply a handler function until it receives all its expected requirements before invoking. Any remaining arguments will be passed on at invocation.
.curry
returns a curried proc. If the optional arity argument is given, it determines the number of arguments. A curried proc receives some arguments. If a sufficient number of arguments are supplied, it passes the supplied arguments to the original proc and returns the result. Otherwise, returns another curried proc that takes the rest of arguments.
# Example 1
l = lambda { |x, y, z| x + y + z }
l.curry[1][2][3] # => 6
# Example 2
a = l.curry[1] # => <Proc:0x007fc759a22920 (lambda)>
b = a[2] # => <Proc:0x007fc759a68b00 (lambda)>
b[3] # => 6
# Better real world example
apply_math = -> fn, a, b { a.send fn, b }
add = apply_math.curry.(:+)
add.(1, 2) # => 3
increment = add.curry.(1)
increment.(1) # => 2
increment.(5) # => 6
the arity of a function or operation is the number of arguments or operands the function or operation accepts
Arity is only useful when using an actual Proc and not a lambda. It's best to think of a lambda like it's an anonymous function; where as a Proc is more like code being 'included' into another chunk of code.
For example if you pass in a Proc to another function then the reason things like the number of arguments you pass to the Proc, and how the Proc's return values work (i.e. returning from a Proc also returns out of the containing function) is because effectively the Proc's code is injected into that other function. But as the lambda acts like a real anonymous function it will error if called with the wrong number of arguments and when executing a return
it'll return out of only that specific block of code and doesn't effect the surrounding function code it was called within.
Now the reason you need to know all this is that being able to use partial application via the arity argument will only work with a Proc. If you use a lambda then by its very nature will throw an error about incorrect number of arguments.
The following is an example of using arity:
p = proc { |x, y, z| x + y + z }
add_to_the_value_three = p.curry(2)
add_to_the_value_three[1][2] # => we're setting up the Proc to have first two args pre-filled (x, y == 1, 2)
# Note: we more likely would've done p.curry(2)[1][2]
add_to_the_value_three[6] # => 9