adt 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|