Module: Fear::PartialFunction
- Included in:
- Combined
- Defined in:
- lib/fear/partial_function.rb,
lib/fear/partial_function/any.rb,
lib/fear/partial_function/empty.rb,
lib/fear/partial_function/guard.rb,
lib/fear/partial_function/lifted.rb,
lib/fear/partial_function/or_else.rb,
lib/fear/partial_function/and_then.rb,
lib/fear/partial_function/combined.rb,
lib/fear/partial_function/guard/or.rb,
lib/fear/partial_function/guard/and.rb,
lib/fear/partial_function/guard/and3.rb
Overview
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:
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)
Defined Under Namespace
Constant Summary collapse
- Empty =
EmptyPartialFunction.new
Class Method Summary collapse
-
.and(*guards, &function) ⇒ Fear::PartialFunction
Creates partial function guarded by several condition.
-
.or(*guards, &function) ⇒ Fear::PartialFunction
Creates partial function guarded by several condition.
Instance Method Summary collapse
- #&(other) ⇒ Object
-
#and_then(other = Utils::UNDEFINED, &block) ⇒ Object
Composes this partial function with a fallback partial function (or Proc) which gets applied where this partial function is not defined.
-
#call(arg) ⇒ any
abstract
Calls this partial function with the given argument when it is contained in the function domain.
-
#call_or_else(arg) {|arg| ... } ⇒ Object
Calls this partial function with the given argument when it is contained in the function domain.
-
#condition ⇒ #===
abstract
describes the domain of partial function.
-
#defined_at?(arg) ⇒ Boolean
Checks if a value is contained in the function’s domain.
- #function ⇒ #call abstract
-
#lift ⇒ #call
Turns this partial function in Proc-like object, returning
Option
. -
#or_else(other) ⇒ PartialFunction
Composes this partial function with a fallback partial function which gets applied where this partial function is not defined.
-
#to_proc ⇒ Proc
Converts this partial function to other.
- #|(other) ⇒ Object
Class Method Details
.and(*guards, &function) ⇒ Fear::PartialFunction
Creates partial function guarded by several condition. All guards should match.
154 155 156 |
# File 'lib/fear/partial_function.rb', line 154 def and(*guards, &function) PartialFunctionClass.new(Guard.and(guards), &function) end |
.or(*guards, &function) ⇒ Fear::PartialFunction
Creates partial function guarded by several condition. Any condition should match.
163 164 165 |
# File 'lib/fear/partial_function.rb', line 163 def or(*guards, &function) PartialFunctionClass.new(Guard.or(guards), &function) end |
Instance Method Details
#&(other) ⇒ Object
138 139 140 |
# File 'lib/fear/partial_function.rb', line 138 def &(other) and_then(other) end |
#and_then(other) ⇒ Fear::PartialFunction #and_then(other) ⇒ Fear::PartialFunction #and_then(&other) ⇒ Fear::PartialFunction
Composes this partial function with a fallback partial function (or Proc) which gets applied where this partial function is not defined.
127 128 129 130 131 132 133 134 135 |
# File 'lib/fear/partial_function.rb', line 127 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(arg) ⇒ any
Returns Calls this partial function with the given argument when it is contained in the function domain.
|
# File 'lib/fear/partial_function.rb', line 56
|
#call_or_else(arg) {|arg| ... } ⇒ Object
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.
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.
80 81 82 83 84 85 86 |
# File 'lib/fear/partial_function.rb', line 80 def call_or_else(arg) if defined_at?(arg) call(arg) else yield arg end end |
#condition ⇒ #===
describes the domain of partial function
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/fear/partial_function.rb', line 47 module PartialFunction # Checks if a value is contained in the function's domain. # # @param arg [any] # @return [Boolean] def defined_at?(arg) condition === arg end # @!method call(arg) # @param arg [any] # @return [any] Calls this partial function with the given argument when it # is contained in the function domain. # @raise [MatchError] when this partial function is not defined. # @abstract # Converts this partial function to other # # @return [Proc] def to_proc proc { |arg| call(arg) } end # 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. # def call_or_else(arg) if defined_at?(arg) call(arg) else yield arg end end # 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 def or_else(other) OrElse.new(self, other) end # @see or_else def |(other) or_else(other) end # 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] # 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 # @see and_then def &(other) and_then(other) end # Turns this partial function in Proc-like object, returning +Option+ # @return [#call] def lift Lifted.new(self) end class << self # Creates partial function guarded by several condition. # All guards should match. # @param guards [<#===>] # @param function [Proc] # @return [Fear::PartialFunction] def and(*guards, &function) PartialFunctionClass.new(Guard.and(guards), &function) end # Creates partial function guarded by several condition. # Any condition should match. # @param guards [<#===>] # @param function [Proc] # @return [Fear::PartialFunction] def or(*guards, &function) PartialFunctionClass.new(Guard.or(guards), &function) end end end |
#defined_at?(arg) ⇒ Boolean
Checks if a value is contained in the function’s domain.
52 53 54 |
# File 'lib/fear/partial_function.rb', line 52 def defined_at?(arg) condition === arg end |
#function ⇒ #call
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/fear/partial_function.rb', line 47 module PartialFunction # Checks if a value is contained in the function's domain. # # @param arg [any] # @return [Boolean] def defined_at?(arg) condition === arg end # @!method call(arg) # @param arg [any] # @return [any] Calls this partial function with the given argument when it # is contained in the function domain. # @raise [MatchError] when this partial function is not defined. # @abstract # Converts this partial function to other # # @return [Proc] def to_proc proc { |arg| call(arg) } end # 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. # def call_or_else(arg) if defined_at?(arg) call(arg) else yield arg end end # 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 def or_else(other) OrElse.new(self, other) end # @see or_else def |(other) or_else(other) end # 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] # 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 # @see and_then def &(other) and_then(other) end # Turns this partial function in Proc-like object, returning +Option+ # @return [#call] def lift Lifted.new(self) end class << self # Creates partial function guarded by several condition. # All guards should match. # @param guards [<#===>] # @param function [Proc] # @return [Fear::PartialFunction] def and(*guards, &function) PartialFunctionClass.new(Guard.and(guards), &function) end # Creates partial function guarded by several condition. # Any condition should match. # @param guards [<#===>] # @param function [Proc] # @return [Fear::PartialFunction] def or(*guards, &function) PartialFunctionClass.new(Guard.or(guards), &function) end end end |
#lift ⇒ #call
Turns this partial function in Proc-like object, returning Option
144 145 146 |
# File 'lib/fear/partial_function.rb', line 144 def lift Lifted.new(self) end |
#or_else(other) ⇒ PartialFunction
Composes this partial function with a fallback partial function which gets applied where this partial function is not defined.
100 101 102 |
# File 'lib/fear/partial_function.rb', line 100 def or_else(other) OrElse.new(self, other) end |
#to_proc ⇒ Proc
Converts this partial function to other
66 67 68 |
# File 'lib/fear/partial_function.rb', line 66 def to_proc proc { |arg| call(arg) } end |
#|(other) ⇒ Object
105 106 107 |
# File 'lib/fear/partial_function.rb', line 105 def |(other) or_else(other) end |