adt 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/adt.rb +39 -0
- data/lib/adt/case_recorder.rb +20 -7
- metadata +1 -1
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -36,7 +36,7 @@ What you now have:
|
|
36
36
|
* A good #== and #inspect implementation
|
37
37
|
* \#_case_? and #when__case_(handle_case_proc, default_proc) for dealing with a single case
|
38
38
|
|
39
|
-
Check the [documentation](http://rubydoc.info/gems/adt/0.0.
|
39
|
+
Check the [documentation](http://rubydoc.info/gems/adt/0.0.3/ADT:cases) for more information.
|
40
40
|
|
41
41
|
Usage examples
|
42
42
|
--------------
|
data/lib/adt.rb
CHANGED
@@ -193,5 +193,44 @@ module ADT
|
|
193
193
|
end
|
194
194
|
end
|
195
195
|
end
|
196
|
+
|
197
|
+
# Defines an operation (method) for an ADT, using a DSL similar to the cases definition.
|
198
|
+
#
|
199
|
+
# For each case in the adt, the block should call a method of the same name, and pass it
|
200
|
+
# a block argument that represents the implementation of the operation for that case.
|
201
|
+
#
|
202
|
+
# eg. To define an operation on a Maybe/Option type which returns the wrapped value, or
|
203
|
+
# the supplied argument if it doesn't have anything:
|
204
|
+
#
|
205
|
+
# class Maybe
|
206
|
+
# extend ADT
|
207
|
+
# cases do
|
208
|
+
# just(:value)
|
209
|
+
# nothing
|
210
|
+
# end
|
211
|
+
#
|
212
|
+
# operation :or_value do |if_nothing|
|
213
|
+
# just { |value| value }
|
214
|
+
# nothing { if_nothing }
|
215
|
+
# end
|
216
|
+
# end
|
217
|
+
#
|
218
|
+
#
|
219
|
+
# @param [Symbol] The name of the operations to define.
|
220
|
+
# @param [Proc] The definitions of the implementations for each case.
|
221
|
+
def operation(sym, &definitions)
|
222
|
+
define_method(sym) do |*args|
|
223
|
+
dsl = CaseRecorder.new
|
224
|
+
dsl_cls = class <<dsl; self; end
|
225
|
+
# This is hax so that we can 'instance_eval' the definitions block on a recorder,
|
226
|
+
# but in this case the definitions block could have arguments (which are arguments
|
227
|
+
# to the operation)
|
228
|
+
dsl_cls.send(:define_method, :_defs, &definitions)
|
229
|
+
dsl._defs(*args)
|
230
|
+
# Now we just turn the [(case_name, impl)] structure into an argument for fold and
|
231
|
+
# are done. Fold with a hash will check that all keys are defined.
|
232
|
+
fold(dsl._implementations.inject({}) { |memo, (c, impl)| memo[c] = impl; memo })
|
233
|
+
end
|
234
|
+
end
|
196
235
|
end
|
197
236
|
|
data/lib/adt/case_recorder.rb
CHANGED
@@ -2,22 +2,35 @@ module ADT
|
|
2
2
|
# @private
|
3
3
|
class CaseRecorder
|
4
4
|
alias :__instance_eval :instance_eval
|
5
|
-
|
6
5
|
instance_methods.each { |m| undef_method m unless m =~ /(^__|object_id)/ }
|
7
6
|
|
8
|
-
attr_reader :_church_cases
|
9
|
-
|
10
7
|
def initialize
|
11
|
-
@
|
8
|
+
@tape = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def _church_cases
|
12
|
+
@tape.map { |xs| xs[0..1] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def _implementations
|
16
|
+
# Implementations have a symbol and a block argument
|
17
|
+
@tape.map { |xs| [xs[0], xs[2]] }
|
12
18
|
end
|
13
19
|
|
14
20
|
# Defines a case for an ADT.
|
15
21
|
def define_case(sym, *args)
|
16
|
-
|
22
|
+
record(sym, *args)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def record(sym, *args, &blk)
|
28
|
+
@tape << [sym, args, blk]
|
17
29
|
end
|
18
30
|
|
19
|
-
|
20
|
-
|
31
|
+
# Records EVERYTHING
|
32
|
+
def method_missing(sym, *args, &blk)
|
33
|
+
record(sym, *args, &blk)
|
21
34
|
end
|
22
35
|
end
|
23
36
|
end
|