Invocation Construction Kit

Get Version

0.3.1

→ ‘ick’

What

The Generalized Greenspun Rule: Any sufficiently complicated platform contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of a functional programming language.

While Ruby provides an ad hoc, informally-specified, bug-ridden, slow implementation of half of higher-order functional programming, it lacks an ad hoc, informally-specified, bug-ridden, slow implementation of half of Monads.

Thus, the Invocation Construction Kit, or “Ick!” Ick provides the tools needed to easily build your own execution abstractions like the “Maybe” monad or the four canonical block evaluators, as well as providing some sugar so you can write things like:

please(sir) { may.i.have.some.more }

Getting Started

sudo gem install ick

Block Structured Ruby

Although Ruby borrows many of its features from Lisp and its syntax from Algol, it does not have block-local variables. In other words, if you declare a variable anywhere inside of a method, that variable is visible everywhere in that method. This is a problem, because it encourages writing methods where the instance variables create lot of dependencies between different expressions. Those methods can be hard to understand and refactor.

Ick solves this problem by providing a block structure method: #let. #let takes an expression and binds it to a variable inside of a block. For example, if you want someone’s phone number only if they are a friend:

let(Person.find(:first, ...)) { |person| person.phone_number if person.friend? }

This code makes it clear that you only need the person variable inside the block. If you want to refactor this code, you know that the entire expression can move without breaking another piece of code. Ick provides three other block structure methods (#returning, #my, and #inside) that are detailed on the Inside Ick page.

Guarded Evaluation

The example above is a common one. Sometimes we want to evaluate a chain of method calls without throwing a NoMethodError if one of the recipients is nil. Sometimes we want to send something a message if and only if it handles the method. There are lots of ad-hoc solutions, including Object#andand. What if you don’t want to install lots of different gems, one for each use?

Ick solves this problem by providing a structure for rolling your own guarded evaluation. You can check for nil, #respond_to?, custom permissions, whatever you like. It looks like this:

class Try < Ick::Guard
  guard_with { |value, sym| value.respond_to?(sym) == true }
  evaluates_in_calling_environment and returns_result
  belongs_to Object
end

try(...) { |sir| sir.may.i.have.some.more }

(Try is built into Ick and was inspired by Chris Wanstrath’s try() and Chalain’s Turtles)

Maybe does exactly the same thing with checking nil rather than whether an object responds to a message:

maybe(...) { |person| person.manager.authority_level.permissions }

Both #try and #maybe are contagious: everything in the chain inside the block is guarded.

More sugar!

If you just want to call a method by name without parameters, the existing blocks work well with Symbol#to_proc:

maybe(Person.find(:first, ...), &:manager)

But you can also use these methods the way Object#andand works:

Person.find(:first, ...).
	maybe.time_cards.
	maybe.map(&:hours_worked).
	maybe.inject(0, &:+)

When you do that, you have to keep calling the method in order to chain them all together, so you might prefer:

maybe(Person.find(:first, ...)) { |p| 
	p.time_cards.map(&:hours_worked).inject(0, &:+) 
}

The Object#andand-style syntax is most useful when you’re just using it for a single method invocation, such as:

Person.find(:first, ...).maybe.salary = 42,000

New in Version 0.3!

Ick version 0.3 includes an experiemental form of let, lets, that allows you to bind multiple variables:

lets(
    :person => Person.find(:first, ...),
    :place  => City.select { ... },
    :thing  => %w(ever loving blue eyed)) {
  "#{person.name} lives in #{place} where he is known as the '#{thing} thing.'"
}

(But I heard that Ick is destroying Ruby‽)

Have no fear of that. Ick will not modify any classes without permission. Out of the box, you cannot call any of Ick’s built in methods the way you see them in these examples. Instead of please(sir) {...} you actually have to call Ick::Please.instance.invoke(sir) {...}. If you want to install one or more of the built-in methods in to the Object class, you call #belongs_to. For example, to install the Maybe and Let methods but no others:

Ick::Maybe.belongs_to Object
Ick::Let.belongs_to Object

You could also install some or all of the methods into a single class where you think you’ll be using them a lot but nowhere else:

class MyAwesomeImplementationOfAsteroids
	[Ick::Let, Ick::Returning, Ick::My, Ick::Inside].each do |clazz|
		clazz.belongs_to self
	end
end

If you simply want everything you see here working exactly as it’s shown, simply call Ick.sugarize once and all of the built-in methods will be installed into Object for you. You can put that in your environment.rb file if you’re using Rails.

The point is, try(program) { responsibly }. You choose which classes to open and which methods to add. All I’m saying is this: before re-opening a class, did you go through the rest of your toolbox first?

Is Ick for me?

Ick does provide a number of very convenient methods for abstracting evaluation. If you adopt them for your project, your code will be more readable, more succinct, and easier to refactor. That being said, it will not make your teeth whiter or help you sleep if you have a newborn.

Every programming problem can be solved with another layer of abstraction, except the problem of too many layers of abstraction

The important caveat about Ick is that its implementation adds abstraction. If all you want are two or three specific methods, writing them as simply and as directly as possible makes for a very simple implementation. Ick uses classes and templates instead of simple methods so that when you are ready to start writing your own abstractions, you can easily use the pieces that Ick has built-in. If Ick was written as a collection of cool methods, you would not be able to extend it without copying and pasting.

Ick raises how you handle things to the level of first-class objects in Ruby, so you can mix and match and separate concerns as you see fit. Logging, permissions, error handling… These are some of the places you can take Ick. Have fun.

Where can I read about what’s going on inside Ick?

Inside Ick

That’s cool, but…

No problem, I get that Ick isn’t exactly what you need. Why not have a look at andand? The andand gem gives you a very specialized version of Object#maybe and an enhanced Object#tap: it does a lot less but tries to do it very, very well. Have a look and let me know what you think.

Administrivia

Home Sweet Home

ick.rubyforge.org

How to submit patches

Read the 8 steps for fixing other people’s code.

The trunk repository is svn://rubyforge.org/var/svn/ick/trunk for anonymous access.

License

This code is free to use under the terms of the MIT license.

Shout Out

Mobile Commons. Still Huge After All These Years.

Contact

Comments are welcome. Send an email to Reginald Braithwaite. And you can always visit weblog.raganwald.com to see what’s cooking.

Reginald Braithwaite, 30th May 2008
Theme extended from Paul Battley