Class: Qo::PatternMatchers::PatternMatch

Inherits:
Object
  • Object
show all
Includes:
Branching
Defined in:
lib/qo/pattern_matchers/pattern_match.rb

Overview

Classic Pattern Match. This matcher uses when and else branches and is meant to be a more powerful variant of the case statement

Author:

  • baweaver

Since:

  • 0.2.0

Direct Known Subclasses

ResultPatternMatch

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Branching

included

Constructor Details

#initialize(destructure: false) {|_self| ... } ⇒ Qo::PatternMatchers::PatternMatch

Creates a new instance of a pattern matcher

Parameters:

  • destructure: (defaults to: false)

    false [Boolean] Whether or not to destructure values before yielding to a block

  • &fn (Proc)

    Function to be used to construct the pattern matcher's branches

Yields:

  • (_self)

Yield Parameters:

Since:

  • 0.2.0



25
26
27
28
29
30
31
# File 'lib/qo/pattern_matchers/pattern_match.rb', line 25

def initialize(destructure: false, &fn)
  @matchers    = []
  @default     = nil
  @destructure = destructure

  yield(self) if block_given?
end

Class Method Details

.create(branches: []) ⇒ Class

Allows for the creation of an anonymous PatternMatcher based on this parent class. To be used by people wishing to make their own pattern matchers with variant branches and other features not included in the defaultly provided ones

Parameters:

  • branches: (defaults to: [])

    [] [Array[Branch]] Branches to be used with this new pattern matcher

Returns:

  • (Class)

    Anonymous pattern matcher class to be bound to a constant or used anonymously.

Since:

  • 0.2.0



44
45
46
47
48
# File 'lib/qo/pattern_matchers/pattern_match.rb', line 44

def self.create(branches: [])
  Class.new(Qo::PatternMatchers::PatternMatch) do
    branches.each { |branch| register_branch(branch.new) }
  end
end

.mixin(destructure: false, as: :match) ⇒ Module

Allows for the injection of a pattern matching function into a type class for direct access, rather than yielding an instance of that class to a pattern matcher.

This is typically done for monadic types that need to match. When combined with extractor type branches it can be very handy for dealing with container types.

Examples:


```ruby
# Technically Some and None don't exist yet, so we have to "cheat" instead
# of just saying `Some` for the precondition
SomeBranch = Qo.create_branch(
  name:        'some',
  precondition: -> v { v.is_a?(Some) },
  extractor:    :value
)

NoneBranch = Qo.create_branch(
  name:        'none',
  precondition: -> v { v.is_a?(None) },
  extractor:    :value
)

SomePatternMatch = Qo.create_pattern_match(branches: [SomeBranch, NoneBranch])

class Some
  include SomePatternMatch.mixin

  attr_reader :value

  def initialize(value) @value = value end

  def fmap(&fn)
    new_value = fn.call(value)
    new_value ? Some.new(new_value) : None(value)
  end
end

class None
  include SomePatternMatch.mixin

  attr_reader :value

  def initialize(value) @value = value end
  def fmap(&fn) None.new(value) end
end

Some.new(1)
  .fmap { |v| v * 2 }
  .match { |m|
    m.some { |v| v + 100 }
    m.none { "OHNO!" }
  }
=> 102

Some.new(1)
  .fmap { |v| nil }
  .match { |m|
    m.some { |v| v + 100 }
    m.none { "OHNO!" }
  }
=> "OHNO!"
```

Parameters:

  • destructure: (defaults to: false)

    false [Boolean] Whether or not to destructure values before yielding to a block

  • as: (defaults to: :match)

    :match [Symbol] Name to use as a method name bound to the including class

Returns:

  • (Module)

    Module to be mixed into a class

Since:

  • 0.2.0



124
125
126
127
128
129
130
131
132
# File 'lib/qo/pattern_matchers/pattern_match.rb', line 124

def self.mixin(destructure: false, as: :match)
  create_self = -> &function { new(destructure: destructure, &function) }

  Module.new do
    define_method(as) do |&function|
      create_self.call(&function).call(self)
    end
  end
end

Instance Method Details

#call(value) ⇒ Any? Also known as: ===, []

Calls the pattern matcher, yielding the target value to the first matching branch it encounters.

Parameters:

  • value (Any)

    Value to match against

Returns:

  • (Any)

    Result of the called branch

  • (nil)

    Returns nil if no branch is matched

Since:

  • 0.2.0



145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/qo/pattern_matchers/pattern_match.rb', line 145

def call(value)
  @matchers.each do |matcher|
    status, return_value = matcher.call(value)
    return return_value if status
  end

  if @default
    _, return_value = @default.call(value)
    return_value
  else
    nil
  end
end

#to_procProc[Any] => Any

Procified version of call

Returns:

  • (Proc[Any] => Any)

Since:

  • 0.2.0



165
166
167
# File 'lib/qo/pattern_matchers/pattern_match.rb', line 165

def to_proc
  -> target { self.call(target) }
end