module Deterministic

TODO: remove dead code

Constants

Option
Result
VERSION

Public Class Methods

guard_context(obj, args) click to toggle source
# File lib/deterministic/enum.rb, line 155
def self.guard_context(obj, args)
  if obj.is_a?(Deterministic::EnumBuilder::DataType::Binary)
    Struct.new(*(args)).new(*(obj.value.values))
  else
    Struct.new(*(args)).new(obj.value)
  end
end
match(obj, &block) click to toggle source
# File lib/deterministic/enum.rb, line 116
def self.match(obj, &block)
  caller_ctx = block.binding.eval 'self'

  matcher = self::Matcher.new(obj)
  matcher.instance_eval(&block)

  variants_in_match = matcher.matches.collect {|e| e[1].name.split('::')[-1].to_sym}.uniq.sort
  variants_not_covered = variants - variants_in_match
  raise Enum::MatchError, "Match is non-exhaustive, #{variants_not_covered} not covered" unless variants_not_covered.empty?

  type_matches = matcher.matches.select { |r| r[0].is_a?(r[1]) }

  type_matches.each { |match|
    obj, type, block, args, guard = match

    if args.count == 0
      return caller_ctx.instance_eval(&block)
    else
      if args.count != obj.args.count
        raise Enum::MatchError, "Pattern (#{args.join(', ')}) must match (#{obj.args.join(', ')})"
      end
      guard_ctx = guard_context(obj, args)

      if guard
        if guard_ctx.instance_exec(obj, &guard)
          return caller_ctx.instance_exec(* obj.wrapped_values, &block)
        end
      else
        return caller_ctx.instance_exec(* obj.wrapped_values, &block)
      end
    end
  }

  raise Enum::MatchError, "No match could be made"
end
new(obj) click to toggle source
# File lib/deterministic/enum.rb, line 169
def initialize(obj)
  @obj = obj
  @matches = []
  @vars = []
end
variants() click to toggle source
# File lib/deterministic/enum.rb, line 152
def self.variants; constants - [:Matcher, :MatchError]; end

Public Instance Methods

+(other) click to toggle source
# File lib/deterministic/option.rb, line 65
def +(other)
  match {
    None() { other }
    Some(where { !other.is_a?(Option) }) {|_| raise TypeError, "Other must be an #{Option}"}
    Some(where { other.some? }) {|s| Option::Some.new(s + other.value) }
    Some() {|_| self }
  }
end
<<(proc=nil, &block)
Alias for: pipe
>=(proc=nil, &block)
Alias for: try
>>(&fn)
Alias for: map
Left(value) click to toggle source
# File lib/deterministic/either.rb, line 28
def Left(value)
  Either.new(Array[value], [])
end
Right(value) click to toggle source
# File lib/deterministic/either.rb, line 32
def Right(value)
  Either.new([], Array[value])
end
and(other) click to toggle source
# File lib/deterministic/result.rb, line 62
def and(other)
  raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
  match {
    Success() {|_| other }
    Failure() {|_| self }
  }
end
and_then(&fn)
Alias for: map
empty?()
Alias for: none?
enum(&block) click to toggle source
# File lib/deterministic/enum.rb, line 112
def enum(&block)
  mod = Class.new do # the enum to be built
    class << self; private :new; end

    def self.match(obj, &block)
      caller_ctx = block.binding.eval 'self'

      matcher = self::Matcher.new(obj)
      matcher.instance_eval(&block)

      variants_in_match = matcher.matches.collect {|e| e[1].name.split('::')[-1].to_sym}.uniq.sort
      variants_not_covered = variants - variants_in_match
      raise Enum::MatchError, "Match is non-exhaustive, #{variants_not_covered} not covered" unless variants_not_covered.empty?

      type_matches = matcher.matches.select { |r| r[0].is_a?(r[1]) }

      type_matches.each { |match|
        obj, type, block, args, guard = match

        if args.count == 0
          return caller_ctx.instance_eval(&block)
        else
          if args.count != obj.args.count
            raise Enum::MatchError, "Pattern (#{args.join(', ')}) must match (#{obj.args.join(', ')})"
          end
          guard_ctx = guard_context(obj, args)

          if guard
            if guard_ctx.instance_exec(obj, &guard)
              return caller_ctx.instance_exec(* obj.wrapped_values, &block)
            end
          else
            return caller_ctx.instance_exec(* obj.wrapped_values, &block)
          end
        end
      }

      raise Enum::MatchError, "No match could be made"
    end

    def self.variants; constants - [:Matcher, :MatchError]; end

    private
    def self.guard_context(obj, args)
      if obj.is_a?(Deterministic::EnumBuilder::DataType::Binary)
        Struct.new(*(args)).new(*(obj.value.values))
      else
        Struct.new(*(args)).new(obj.value)
      end
    end
  end
  enum = EnumBuilder.new(mod)
  enum.instance_eval(&block)

  type_variants = mod.constants

  matcher = Class.new {
    def initialize(obj)
      @obj = obj
      @matches = []
      @vars = []
    end

    attr_reader :matches, :vars

    def where(&guard)
      guard
    end

    type_variants.each { |m|
      define_method(m) { |guard = nil, &block|
        raise ArgumentError, "No block given to `#{m}`" if block.nil?
        params_spec = block.parameters
        if params_spec.any? {|spec| spec.size < 2 }
          raise ArgumentError, "Unnamed param found in block parameters: #{params_spec.inspect}"
        end
        if params_spec.any? {|spec| spec[0] != :req && spec[0] != :opt }
          raise ArgumentError, "Only :req & :opt params allowed; parameters=#{params_spec.inspect}"
        end
        args = params_spec.map {|spec| spec[1] }

        type = Kernel.eval("#{mod.name}::#{m}")

        if guard && !guard.is_a?(Proc)
          guard = nil
        end

        @matches << [@obj, type, block, args, guard]
      }
    }
  }

  mod.const_set(:Matcher, matcher)

  type_variants.each { |variant|
    mod.singleton_class.class_exec {
      define_method(variant) { |*args|
        const_get(variant).new(*args)
      }
    }
  }
  mod
end
failure?() click to toggle source
# File lib/deterministic/result.rb, line 50
def failure?
  is_a? Result::Failure
end
fmap(&fn) click to toggle source
# File lib/deterministic/option.rb, line 30
def fmap(&fn)
  match {
    Some() {|s| self.class.new(fn.(s)) }
    None() {    self }
  }
end
impl(enum_type, &block) click to toggle source
# File lib/deterministic/enum.rb, line 216
def impl(enum_type, &block)
  enum_type.variants.each { |v|
    name = "#{enum_type.name}::#{v.to_s}"
    type = Kernel.eval(name)
    type.class_eval(&block)
  }
end
instance(protocol, type, &block) click to toggle source
# File lib/deterministic/protocol.rb, line 94
def instance(protocol, type, &block)
  InstanceBuilder.new(protocol, type, block).build
end
map(&fn) click to toggle source
# File lib/deterministic/option.rb, line 37
def map(&fn)
  match {
    Some() {|s| self.bind(&fn) }
    None() {    self }
  }
end
Also aliased as: >>, and_then
map_err(proc=nil, &block) click to toggle source
# File lib/deterministic/result.rb, line 30
def map_err(proc=nil, &block)
  match {
    Success() {|_| self }
    Failure() {|_| self.bind(proc|| block) }
  }
end
Also aliased as: or_else
none?() click to toggle source
# File lib/deterministic/option.rb, line 48
def none?
  is_a? Option::None
end
Also aliased as: empty?
or(other) click to toggle source
# File lib/deterministic/result.rb, line 54
def or(other)
  raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
  match {
    Success() {|_| self }
    Failure() {|_| other }
  }
end
or_else(proc=nil, &block)
Alias for: map_err
pipe(proc=nil, &block) click to toggle source
# File lib/deterministic/result.rb, line 39
def pipe(proc=nil, &block)
  (proc || block).call(self)
  self
end
Also aliased as: <<
protocol(typevar, &block) click to toggle source
# File lib/deterministic/protocol.rb, line 88
def protocol(typevar, &block)
  protocol = ProtocolBuilder.new(typevar, block).build
  p_module = block.binding.eval('self')
  p_module.const_set(:Protocol, protocol)
end
some?() click to toggle source
# File lib/deterministic/option.rb, line 44
def some?
  is_a? Option::Some
end
success?() click to toggle source
# File lib/deterministic/result.rb, line 46
def success?
  is_a? Result::Success
end
try(proc=nil, &block) click to toggle source
# File lib/deterministic/result.rb, line 80
def try(proc=nil, &block)
  map(proc, &block)
rescue => err
  Result::Failure.new(err)
end
Also aliased as: >=
value_or(n) click to toggle source
# File lib/deterministic/option.rb, line 54
def value_or(n)
  match {
    Some() {|s| s }
    None() {    n }
  }
end
value_to_a() click to toggle source
# File lib/deterministic/option.rb, line 61
def value_to_a
  @value
end
where(&guard) click to toggle source
# File lib/deterministic/enum.rb, line 177
def where(&guard)
  guard
end