h1. Invocation Construction Kit
h1. → 'ick'
h2. 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:
<pre syntax="ruby">
please(sir) { may.i.have.some.more }
</pre>
h2. Getting Started
<pre>sudo gem install ick</pre>
h2. 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:
<pre syntax="ruby">
let(Person.find(:first, ...)) { |person| person.phone_number if person.friend? }
</pre>
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":inside.html page.
h2. 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":http://andand.rubyforge.org. 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:
<pre syntax="ruby">
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 }
</pre>
(Try is built into Ick and was inspired by Chris Wanstrath's "try()":http://ozmm.org/posts/try.html and Chalain's "Turtles":http://chalain.livejournal.com/66798.html)
Maybe does exactly the same thing with checking nil rather than whether an object responds to a message:
<pre syntax="ruby">
maybe(...) { |person| person.manager.authority_level.permissions }
</pre>
Both #try and #maybe are _contagious_: everything in the chain inside the block is guarded.
h2. More sugar!
If you just want to call a method by name without parameters, the existing blocks work well with Symbol#to_proc:
<pre syntax="ruby">
maybe(Person.find(:first, ...), &:manager)
</pre>
But you can also use these methods the way Object#andand works:
<pre syntax="ruby">
Person.find(:first, ...).
maybe.time_cards.
maybe.map(&:hours_worked).
maybe.inject(0, &:+)
</pre>
When you do that, you have to keep calling the method in order to chain them all together, so you might prefer:
<pre syntax="ruby">
maybe(Person.find(:first, ...)) { |p|
p.time_cards.map(&:hours_worked).inject(0, &:+)
}
</pre>
The Object#andand-style syntax is most useful when you're just using it for a single method invocation, such as:
<pre syntax="ruby">
Person.find(:first, ...).maybe.salary = 42,000
</pre>
h3. New in Version 0.3!
Ick version 0.3 includes an experiemental form of let, @lets@, that allows you to bind multiple variables:
<pre syntax="ruby">
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.'"
}
</pre>
h3. (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:
<pre syntax="ruby">
Ick::Maybe.belongs_to Object
Ick::Let.belongs_to Object
</pre>
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:
<pre syntax="ruby">
class MyAwesomeImplementationOfAsteroids
[Ick::Let, Ick::Returning, Ick::My, Ick::Inside].each do |clazz|
clazz.belongs_to self
end
end
</pre>
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?":http://avdi.org/devblog/2008/02/25/full-disclosure/
h2. 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.
h2. Where can I read about what's going on inside Ick?
"Inside Ick":inside.html
h2. That's cool, but…
No problem, I get that Ick isn't exactly what you need. Why not have a look at "andand":http://andand.rubyforge.org? 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.
h2. Administrivia
h3. Home Sweet Home
"ick.rubyforge.org":http://ick.rubyforge.org
h3. How to submit patches
Read the "8 steps for fixing other people's code":http://drnicwilliams.com/2007/06/01/8-steps-for-fixing-other-peoples-code/.
The trunk repository is @svn://rubyforge.org/var/svn/ick/trunk@ for anonymous access.
h3. License
This code is free to use under the terms of the "MIT license":http://en.wikipedia.org/wiki/MIT_License.
h3. Shout Out
"Mobile Commons":http://mcommons.com/. Still Huge After All These Years.
h3. Contact
Comments are welcome. Send an email to "Reginald Braithwaite":mailto:raganwald+rubyforge@gmail.com. And you can always visit "weblog.raganwald.com":http://weblog.raganwald.com/ to see what's cooking.