Method: ADT.operation
- Defined in:
- lib/adt.rb
.operation(sym, &definitions) ⇒ Object
Defines an operation (method) for an ADT, using a DSL similar to the cases definition.
For each case in the adt, the block should call a method of the same name, and pass it a block argument that represents the implementation of the operation for that case.
eg. To define an operation on a Maybe/Option type which returns the wrapped value, or the supplied argument if it doesn’t have anything:
class Maybe
extend ADT
cases do
just(:value)
nothing
end
operation :or_value do |if_nothing|
just { |value| value }
nothing { if_nothing }
end
end
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/adt.rb', line 224 def operation(sym, &definitions) define_method(sym) do |*args| the_instance = self dsl = CaseRecorder.new # The definitions block needs to be executed in the context of the recorder, to # read the impls. dsl.__instance_exec(*args, &definitions) # Now we just turn the [(case_name, impl)] structure into an argument for fold and # are done. Fold with a hash will check that all keys are defined. fold(dsl._implementations.inject({}) { |memo, (c, impl)| # Fucker. if 'impl' is used directly, because of the 'define_method' from earlier, # it is evaluated in the context of the recorder, which is bad. So instead. We # instance_exec it back on the instance. # TODO: use the proc builder like in the `cases` method, which will let us tie # down the arity some_impl = lambda { |*args| the_instance.instance_exec(*args, &impl) } memo[c] = some_impl memo }) end end |