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.
@@ -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