module Obfusk::ADT::ClassMethods

Public Instance Methods

_new(ctor, data = {}, &b) click to toggle source
# File lib/obfusk/adt.rb, line 41
def _new(ctor, data = {}, &b)
  c = constructors[ctor]
  c[:method].call *data.values_at(*c[:keys]), &b
end
constructors() click to toggle source

the constructors

# File lib/obfusk/adt.rb, line 106
def constructors
  @constructors ||= {}
end
import_constructors(scope, const = true) click to toggle source

import the constructors into another namespace

# File lib/obfusk/adt.rb, line 111
def import_constructors(scope, const = true)
  constructors.each_pair do |k,v|
    m = method k
    scope.define_singleton_method(k) { |*a,&b| m[*a,&b] }
    scope.const_set k, v[:ctor] if const
  end
end
inherited(subclass) click to toggle source

duplicate constructors for subclasses

# File lib/obfusk/adt.rb, line 47
def inherited(subclass)
  return if ::Obfusk::ADT_Meta__[:inheriting].last
  ctors = constructors
  subclass.class_eval do
    ctors.each_pair do |k,v|
      constructor v[:name], *v[:keys], &v[:block]
    end
  end
end
match(x, opts) click to toggle source

pattern matching

# File lib/obfusk/adt.rb, line 120
def match(x, opts)
  raise ArgumentError, 'not an ADT' unless x.is_a?(::Obfusk::ADT)
  raise ArgumentError,
    "types do not match (#{x.class.superclass} for #{self})" \
      unless x.class.superclass == self
  x.match opts
end
new(*a, &b) click to toggle source

record constructor; call with name of constructor and hash of keys to values

Calls superclass method
# File lib/obfusk/adt.rb, line 37
def new(*a, &b)
  ancestors.include?(::Obfusk::ADT::Constructor) ? super : _new(*a, &b)
end

Private Instance Methods

constructor(name, *keys, &b) click to toggle source

create a constructor @param [Symbol] name the name of the constructor @param [<Symbol>] keys the keys of the constructor

# File lib/obfusk/adt.rb, line 60
def constructor(name, *keys, &b)
  keys_ = keys.map(&:to_sym)
  name_ = name.to_sym
  ctor  = ::Obfusk::ADT_Meta__[:mutex].synchronize do
    begin
      ::Obfusk::ADT_Meta__[:inheriting] << true
      Class.new self
    ensure
      ::Obfusk::ADT_Meta__[:inheriting].pop
    end
  end
  ctor.class_eval do
    include ::Obfusk::ADT::Constructor
    keys_.each { |k| define_method(k) { __adt_data__[k] } }
    define_method(:initialize) do |guard, ctor, *values, &f|
      raise ArgumentError, 'for internal use only!' \
        unless guard == :for_internal_use_only
      if !b && (k = keys_.length) != (v = values.length)
        raise ArgumentError, "wrong number of arguments (#{v} for #{k})"
      end
      data  = Hash[keys_.zip values].freeze
      @ctor = ctor ; @ctor_name = name_ ; @ctor_keys = keys_
      @data = b ? b[self, data, values, f].freeze : data
    end
  end
  class_eval do
    const_set name_, ctor
    f = -> v, b { ctor.new :for_internal_use_only, ctor, *v, &b }
    if !b && keys.empty?
      singleton = f[[],nil]
      define_singleton_method(name_) { singleton }
      define_method(name_)           { singleton }
    else
      define_singleton_method(name_) { |*values,&b| f[values,b] }
      define_method(name_)           { |*values,&b| f[values,b] }
    end
    constructors[name_] = {
      ctor: ctor, method: method(name_), name: name_,
      keys: keys_, block: b
    }
  end
  name_
end