A couple months ago, I wrote a gem called ProcParty. When included in a
class, it lets you call #to_proc
on instances of the class.
Here’s a quick example:
class Exponenter
include ProcParty
def initialize(power)
@power = power
end
def call(n)
n ** @power
end
end
[1, 2, 3].map(&Exponenter.new(2)) # => [1, 4, 9]
That example seems kinda lame, but it becomes pretty powerful since classes can have initializers. This lets us, in a sense, use partial application in a very natural, Ruby-like way
squarer = Exponenter.new(2)
cuber = Exponenter.new(3)
[1, 2, 3].map(&squarer) # => [1, 4, 9]
[1, 2, 3].map(&cuber) # => [1, 8, 27]
This is equivalent to the traditional functional style (this example uses Ruby procs)
exponenter = -> (power, n) { n ** power }
squarer = exponenter.curry[2]
cuber = exponenter.curry[3]
[1, 2, 3].map(&squarer) # => [1, 4, 9]
[1, 2, 3].map(&cuber) # => [1, 8, 27]
The &
operator tries to convert a Proc to a block. If the object isn’t already
a Proc, it first calls #to_proc
on that object, then converts the result into
a block.
[1, Kernel, Object.new].each { |object| object.inspect }
# => [1, Kernel, #<Object:0x007febe9000a50>]
[1, Kernel, Object.new].each(&-> (object) { object.inspect })
# => [1, Kernel, #<Object:0x007febe8039cc0>]
[1, Kernel, Object.new].each(&:inspect)
# => [1, Kernel, #<Object:0x007febe8059250>]
funny_example = -> (object) { object.inspect }
def funny_example.to_proc
-> { "overriden inspection! JK" }
end
[1, Kernel, Object.new].each(&funny_example)
# => [1, Kernel, #<Object:0x007febe8021c88>]
# Note that the original wasn't "overridden". It used the original since it
# was already a Proc
Since the &
operator calls #to_proc
on the object, we can define any
arbitrary object to return any arbitrary proc and have that run as a block.
class Inspected
def to_proc
-> (object) { object.inspect }
end
end
[1, Kernel, Object.new].each(&Inspected.new)
# => [1, Kernel, #<Object:0x007f962fb3c5a0>]
The core functionality of ProcParty boils down to this idea. It uses
Method#to_proc
to convert the class’ #call
method into a Proc, which is then
converted into a block
class Inspected
def call(object)
object.inspect
end
def to_proc
method(:call).to_proc
end
end
[1, Kernel, Object.new].each(&Inspected.new)
# => [1, Kernel, #<Object:0x007f962fb95b78>]
As a side note, I find it extremely interesting that it’s possible to maintain statelessness in functional programming by using currying/partial application. In a functional language, the example with currying is considered stateless. However, the example with the initializer is considered stateful in an object oriented language.
I personally like to think of these classes as stateless, or at a minimum, that it’s free of mutable state.
ProcParty is on RubyGems and on Github. Please let me know what you think!