module Fear::PartialFunction

A partial function is a unary function defined on subset of all possible inputs. The method defined_at? allows to test dynamically if an arg is in the domain of the function.

Even if defined_at? returns true for given arg, calling call may still throw an exception, so the following code is legal:

@example
  Fear.case(->(_) { true }) { 1/0 }

It is the responsibility of the caller to call defined_at? before calling call, because if defined_at? is false, it is not guaranteed call will throw an exception to indicate an error guard. If an exception is not thrown, evaluation may result in an arbitrary arg.

The main distinction between PartialFunction and Proc is that the user of a PartialFunction may choose to do something different with input that is declared to be outside its domain. For example:

@example

sample = 1...10

is_even = Fear.case(->(arg) { arg % 2 == 0}) do |arg|
  "#{arg} is even"
end

is_odd = Fear.case(->(arg) { arg % 2 == 1}) do |arg|
  "#{arg} is odd"
end

The method or_else allows chaining another partial function to handle input outside the declared domain

numbers = sample.map(is_even.or_else(is_odd).to_proc)

@see github.com/scala/scala/commit/5050915eb620af3aa43d6ddaae6bbb83ad74900d @!method condition

describes the domain of partial function
@return [#===]
@abstract

@!method function

@return [#call]
@abstract

Constants

EMPTY

Public Class Methods

and(*guards, &function) click to toggle source

Creates partial function guarded by several condition. All guards should match. @param guards [<#===>] @param function [Proc] @return [Fear::PartialFunction]

# File lib/fear/partial_function.rb, line 162
def and(*guards, &function)
  PartialFunctionClass.new(Guard.and(guards), &function)
end
or(*guards, &function) click to toggle source

Creates partial function guarded by several condition. Any condition should match. @param guards [<#===>] @param function [Proc] @return [Fear::PartialFunction]

# File lib/fear/partial_function.rb, line 171
def or(*guards, &function)
  PartialFunctionClass.new(Guard.or(guards), &function)
end

Public Instance Methods

&(other) click to toggle source

@see and_then

# File lib/fear/partial_function.rb, line 146
def &(other)
  and_then(other)
end
and_then(other = Utils::UNDEFINED, &block) click to toggle source

Composes this partial function with a fallback partial function (or Proc) which gets applied where this partial function is not defined.

@overload and_then(other)

@param other [Fear::PartialFunction]
@return [Fear::PartialFunction] a partial function with the same domain as this partial function, which maps
  argument +x+ to +other.(self.call(x))+.
@note calling +#defined_at?+ on the resulting partial function may call the first
  partial function and execute its side effect. It is highly recommended to call +#call_or_else+
  instead of +#defined_at?+/+#call+ for efficiency.

@overload and_then(other)

@param other [Proc]
@return [Fear::PartialFunction] a partial function with the same domain as this partial function, which maps
  argument +x+ to +other.(self.call(x))+.

@overload and_then(&other)

@param other [Proc]
@return [Fear::PartialFunction]
# File lib/fear/partial_function.rb, line 135
def and_then(other = Utils::UNDEFINED, &block)
  Utils.with_block_or_argument("Fear::PartialFunction#and_then", other, block) do |fun|
    if fun.is_a?(Fear::PartialFunction)
      Combined.new(self, fun)
    else
      AndThen.new(self, &fun)
    end
  end
end
call_or_else(arg) { |arg| ... } click to toggle source

Calls this partial function with the given argument when it is contained in the function domain. Calls fallback function where this partial function is not defined.

@param arg [any] @yield [arg] if partial function not defined for this arg

@note that expression +pf.call_or_else(arg, &fallback)+ is equivalent to

+pf.defined_at?(arg) ? pf.(arg) : fallback.(arg)+
except that +call_or_else+ method can be implemented more efficiently to avoid calling +defined_at?+ twice.
# File lib/fear/partial_function.rb, line 88
def call_or_else(arg)
  if defined_at?(arg)
    call(arg)
  else
    yield arg
  end
end
defined_at?(arg) click to toggle source

Checks if a value is contained in the function's domain.

@param arg [any] @return [Boolean]

# File lib/fear/partial_function.rb, line 60
def defined_at?(arg)
  condition === arg
end
lift() click to toggle source

Turns this partial function in Proc-like object, returning Option @return [#call]

# File lib/fear/partial_function.rb, line 152
def lift
  Lifted.new(self)
end
or_else(other) click to toggle source

Composes this partial function with a fallback partial function which gets applied where this partial function is not defined.

@param other [PartialFunction] @return [PartialFunction] a partial function which has as domain the union of the domains

of this partial function and +other+.

@example

handle_even = Fear.case(:even?.to_proc) { |x| "#{x} is even" }
handle_odd = Fear.case(:odd?.to_proc) { |x| "#{x} is odd" }
handle_even_or_odd = handle_even.or_else(odd)
handle_even_or_odd.(42) #=> 42 is even
handle_even_or_odd.(42) #=> 21 is odd
# File lib/fear/partial_function.rb, line 108
def or_else(other)
  OrElse.new(self, other)
end
to_proc() click to toggle source

Converts this partial function to other

@return [Proc]

# File lib/fear/partial_function.rb, line 74
def to_proc
  proc { |arg| call(arg) }
end
|(other) click to toggle source

@see or_else

# File lib/fear/partial_function.rb, line 113
def |(other)
  or_else(other)
end