raganwald / ick

The Invocation Construction Kit: an ad hoc, informally-specified, bug-ridden, slow implementation of half of Monads.

This URL has Read+Write access

nizze (author)
Tue Mar 24 05:26:25 -0700 2009
raganwald (committer)
Mon Apr 13 07:49:18 -0700 2009
ick / website / index.txt
100755 166 lines (104 sloc) 8.241 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
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&#8253;)
 
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&hellip;
 
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.