colstrom / invocations

Because sometimes Proc#curry just isn't enough...

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Invocations

Overview

Invocations are better partial functions for Ruby, because sometimes Proc#curry just isn’t enough.

What problem does this solve?

Partial function evaluation is a very useful tool, but understanding the state carried by a curried Proc is often cumbersome and unintuitive.

How does Invocations address this problem?

Invocations provides a single class: the Invocation. It functions as a drop-in alternative to a Proc, with a few notable improvements (none of which break compatibility):

  • An Invocation is implicitly self-currying. Which is to say that if you call it without all the parameters it requires, it simply returns an Invocation that requires the missing parameters.
  • An Invocation allows non-keyword arguments to be given as keywords.
  • An Invocation allows arguments to be given out of order, as keyword arguments.
  • An Invocation has several methods for inspection, including ones for listing the missing arguments, identifying keyword inferences, explaining how the underlying function will be called, and reporting internal state.

Installation

gem install invocations

How can I start using this majestic tool?

An Invocation is a drop-in alternative to Proc, or lambda. You can use it as an explicit &block, etc. As a result, it’s very easy to adapt existing code to use it.

Let’s lay out a simple function that will serve as our example, going forward.

This power function takes two arguments, n (a number) and e (an exponent) and returns the result of raising n to e:

lambda_power = lambda { |n, e| n ** e }

Now, this is somewhat contrived, because I’ve deliberately defined the arguments in the least convenient order, for illustrative purposes.

The equivalent Invocation would be:

invoke_power = Invocation.new { |n, e| n ** e }

Invocations includes an optional Refinement for that brings the syntax more in line with proc and lambda:

using Invocations
invoke_power = invocation { |n, e| n ** e }

Calling either of these is the same:

lambda_power.(5, 2) #=> 25
invoke_power.(5, 2) #=> 25

Let’s say we wanted to define lambda_square and lambda_cube functions, that do what their names imply:

lambda_square = lambda { |n| lambda_power.(n, 2) }
lambda_cube   = lambda { |n| lambda_power.(n, 3) }

The order of the arguments to lambda_power makes these definitions more awkward. If instead, we had defined it like so:

lambda_power = lambda { |e, n| n ** e }.curry

Then we could have done this:

lambda_square = lambda_power.(2)
lambda_cube   = lambda_power.(3)

That said, we don’t define every function we use. Often we use the functions provided by a library, and if those have inconvenient argument ordering, too bad.

If we had been using an Invocation, we could have done this:

invoke_square = invoke_power.(e: 2)
invoke_cube   = invoke_power.(e: 3)

Wait what? Those weren’t keyword arguments.

True, but the block parameters have names. Since it is a SyntaxError for a block to have two parameters with the same name, an Invocation can Do The Right Thing.

Explore It!

Invocations really shines when used with a great REPL like pry.

I’ve uploaded a short screencast here that demonstrates the sort of information an Invocation provides (using the example scenario above).

License

Invocations is available under the MIT License. See LICENSE.txt for the full text.

Contributors

About

Because sometimes Proc#curry just isn't enough...

License:MIT License


Languages

Language:Ruby 100.0%