adt 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ 0.0.3
2
+ =====
3
+
4
+ * Added a DSL for declaring operations.
1
5
 
2
6
  0.0.2
3
7
  =====
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.2/ADT:cases) for more information.
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
 
@@ -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
- @_church_cases = []
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
- @_church_cases << [sym, args]
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
- def method_missing(sym, *args)
20
- define_case(sym, *args)
31
+ # Records EVERYTHING
32
+ def method_missing(sym, *args, &blk)
33
+ record(sym, *args, &blk)
21
34
  end
22
35
  end
23
36
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: adt
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.2
5
+ version: 0.0.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Nick Partridge