fear 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +30 -4
- data/.travis.yml +2 -3
- data/Appraisals +5 -9
- data/CHANGELOG.md +9 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +255 -85
- data/Rakefile +393 -0
- data/fear.gemspec +13 -6
- data/gemfiles/dry_equalizer_0.1.0.gemfile +1 -0
- data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +31 -27
- data/gemfiles/dry_equalizer_0.2.1.gemfile +1 -0
- data/gemfiles/dry_equalizer_0.2.1.gemfile.lock +31 -27
- data/lib/fear/either.rb +49 -14
- data/lib/fear/either_pattern_match.rb +48 -0
- data/lib/fear/empty_partial_function.rb +36 -0
- data/lib/fear/failure.rb +5 -4
- data/lib/fear/failure_pattern_match.rb +8 -0
- data/lib/fear/for.rb +46 -51
- data/lib/fear/left.rb +7 -1
- data/lib/fear/left_pattern_match.rb +9 -0
- data/lib/fear/none.rb +37 -2
- data/lib/fear/none_pattern_match.rb +12 -0
- data/lib/fear/option.rb +65 -31
- data/lib/fear/option_pattern_match.rb +45 -0
- data/lib/fear/partial_function/and_then.rb +48 -0
- data/lib/fear/partial_function/any.rb +26 -0
- data/lib/fear/partial_function/combined.rb +51 -0
- data/lib/fear/partial_function/empty.rb +6 -0
- data/lib/fear/partial_function/guard/and.rb +36 -0
- data/lib/fear/partial_function/guard/and3.rb +39 -0
- data/lib/fear/partial_function/guard/or.rb +36 -0
- data/lib/fear/partial_function/guard.rb +90 -0
- data/lib/fear/partial_function/lifted.rb +20 -0
- data/lib/fear/partial_function/or_else.rb +62 -0
- data/lib/fear/partial_function.rb +171 -0
- data/lib/fear/partial_function_class.rb +26 -0
- data/lib/fear/pattern_match.rb +102 -0
- data/lib/fear/pattern_matching_api.rb +110 -0
- data/lib/fear/right.rb +7 -1
- data/lib/fear/right_biased.rb +2 -12
- data/lib/fear/right_pattern_match.rb +9 -0
- data/lib/fear/some.rb +5 -2
- data/lib/fear/some_pattern_match.rb +11 -0
- data/lib/fear/success.rb +5 -4
- data/lib/fear/success_pattern_match.rb +10 -0
- data/lib/fear/try.rb +56 -16
- data/lib/fear/try_pattern_match.rb +28 -0
- data/lib/fear/utils.rb +24 -14
- data/lib/fear/version.rb +1 -1
- data/lib/fear.rb +21 -4
- data/spec/fear/either_pattern_match_spec.rb +37 -0
- data/spec/fear/failure_spec.rb +41 -3
- data/spec/fear/for_spec.rb +17 -29
- data/spec/fear/guard_spec.rb +101 -0
- data/spec/fear/left_spec.rb +38 -0
- data/spec/fear/none_spec.rb +80 -0
- data/spec/fear/option_pattern_match_spec.rb +35 -0
- data/spec/fear/partial_function/empty_spec.rb +36 -0
- data/spec/fear/partial_function_and_then_spec.rb +145 -0
- data/spec/fear/partial_function_composition_spec.rb +80 -0
- data/spec/fear/partial_function_or_else_spec.rb +274 -0
- data/spec/fear/partial_function_spec.rb +165 -0
- data/spec/fear/pattern_match_spec.rb +59 -0
- data/spec/fear/right_biased/left.rb +1 -6
- data/spec/fear/right_biased/right.rb +0 -5
- data/spec/fear/right_spec.rb +38 -0
- data/spec/fear/some_spec.rb +37 -0
- data/spec/fear/success_spec.rb +41 -4
- data/spec/fear/try_pattern_match_spec.rb +37 -0
- metadata +97 -23
- data/lib/fear/for/evaluation_context.rb +0 -91
data/lib/fear/option.rb
CHANGED
@@ -11,12 +11,9 @@ module Fear
|
|
11
11
|
# having to check for the existence of a value.
|
12
12
|
#
|
13
13
|
# @example A less-idiomatic way to use +Option+ values is via pattern matching
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# puts name.strip.upcase
|
18
|
-
# when None
|
19
|
-
# puts 'No name value'
|
14
|
+
# Option(params[:name]).match do |m|
|
15
|
+
# m.some { |name| name.strip.upcase }
|
16
|
+
# m.none { 'No name value' }
|
20
17
|
# end
|
21
18
|
#
|
22
19
|
# @example or manually checking for non emptiness
|
@@ -35,20 +32,20 @@ module Fear
|
|
35
32
|
# @return [any]
|
36
33
|
# @example
|
37
34
|
# Some(42).get_or_else { 24/2 } #=> 42
|
38
|
-
# None
|
35
|
+
# None.get_or_else { 24/2 } #=> 12
|
39
36
|
# @overload get_or_else(default)
|
40
37
|
# @return [any]
|
41
38
|
# @example
|
42
39
|
# Some(42).get_or_else(12) #=> 42
|
43
|
-
# None
|
40
|
+
# None.get_or_else(12) #=> 12
|
44
41
|
#
|
45
42
|
# @!method or_else(&alternative)
|
46
43
|
# Returns this +Some+ or the given alternative if this is a +None+.
|
47
44
|
# @return [Option]
|
48
45
|
# @example
|
49
46
|
# Some(42).or_else { Some(21) } #=> Some(42)
|
50
|
-
# None
|
51
|
-
# None
|
47
|
+
# None.or_else { Some(21) } #=> Some(21)
|
48
|
+
# None.or_else { None } #=> None
|
52
49
|
#
|
53
50
|
# @!method include?(other_value)
|
54
51
|
# Returns +true+ if it has an element that is equal
|
@@ -58,7 +55,7 @@ module Fear
|
|
58
55
|
# @example
|
59
56
|
# Some(17).include?(17) #=> true
|
60
57
|
# Some(17).include?(7) #=> false
|
61
|
-
# None
|
58
|
+
# None.include?(17) #=> false
|
62
59
|
#
|
63
60
|
# @!method each(&block)
|
64
61
|
# Performs the given block if this is a +Some+.
|
@@ -70,7 +67,7 @@ module Fear
|
|
70
67
|
# puts value
|
71
68
|
# end #=> prints 17
|
72
69
|
#
|
73
|
-
# None
|
70
|
+
# None.each do |value|
|
74
71
|
# puts value
|
75
72
|
# end #=> does nothing
|
76
73
|
#
|
@@ -81,7 +78,7 @@ module Fear
|
|
81
78
|
# @yieldreturn [any]
|
82
79
|
# @example
|
83
80
|
# Some(42).map { |v| v/2 } #=> Some(21)
|
84
|
-
# None
|
81
|
+
# None.map { |v| v/2 } #=> None
|
85
82
|
#
|
86
83
|
# @!method flat_map(&block)
|
87
84
|
# Returns the given block applied to the value from this +Some+
|
@@ -91,15 +88,7 @@ module Fear
|
|
91
88
|
# @return [Option]
|
92
89
|
# @example
|
93
90
|
# Some(42).flat_map { |v| Some(v/2) } #=> Some(21)
|
94
|
-
# None
|
95
|
-
#
|
96
|
-
# @!method to_a
|
97
|
-
# Returns an +Array+ containing the +Some+ value or an
|
98
|
-
# empty +Array+ if this is a +None+
|
99
|
-
# @return [Array]
|
100
|
-
# @example
|
101
|
-
# Some(42).to_a #=> [21]
|
102
|
-
# None().to_a #=> []
|
91
|
+
# None.flat_map { |v| Some(v/2) } #=> None
|
103
92
|
#
|
104
93
|
# @!method any?(&predicate)
|
105
94
|
# Returns +false+ if +None+ or returns the result of the
|
@@ -110,7 +99,7 @@ module Fear
|
|
110
99
|
# @example
|
111
100
|
# Some(12).any?( |v| v > 10) #=> true
|
112
101
|
# Some(7).any?( |v| v > 10) #=> false
|
113
|
-
# None
|
102
|
+
# None.any?( |v| v > 10) #=> false
|
114
103
|
#
|
115
104
|
# @!method select(&predicate)
|
116
105
|
# Returns self if it is nonempty and applying the predicate to this
|
@@ -120,8 +109,8 @@ module Fear
|
|
120
109
|
# @return [Option]
|
121
110
|
# @example
|
122
111
|
# Some(42).select { |v| v > 40 } #=> Success(21)
|
123
|
-
# Some(42).select { |v| v < 40 } #=> None
|
124
|
-
# None
|
112
|
+
# Some(42).select { |v| v < 40 } #=> None
|
113
|
+
# None.select { |v| v < 40 } #=> None
|
125
114
|
#
|
126
115
|
# @!method reject(&predicate)
|
127
116
|
# Returns +Some+ if applying the predicate to this
|
@@ -132,7 +121,7 @@ module Fear
|
|
132
121
|
# @example
|
133
122
|
# Some(42).reject { |v| v > 40 } #=> None
|
134
123
|
# Some(42).reject { |v| v < 40 } #=> Some(42)
|
135
|
-
# None
|
124
|
+
# None.reject { |v| v < 40 } #=> None
|
136
125
|
#
|
137
126
|
# @!method get
|
138
127
|
# @return [any] the +Option+'s value.
|
@@ -143,14 +132,31 @@ module Fear
|
|
143
132
|
# @return [Boolean]
|
144
133
|
# @example
|
145
134
|
# Some(42).empty? #=> false
|
146
|
-
# None
|
135
|
+
# None.empty? #=> true
|
136
|
+
#
|
137
|
+
# @!method match(&matcher)
|
138
|
+
# Pattern match against this +Option+
|
139
|
+
# @yield matcher [Fear::OptionPatternMatch]
|
140
|
+
# @example
|
141
|
+
# Option(val).match do |m|
|
142
|
+
# m.some(Integer) do |x|
|
143
|
+
# x * 2
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# m.some(String) do |x|
|
147
|
+
# x.to_i * 2
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# m.none { 'NaN' }
|
151
|
+
# m.else { 'error '}
|
152
|
+
# end
|
147
153
|
#
|
148
154
|
# @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/Option.scala
|
149
155
|
#
|
150
156
|
module Option
|
151
157
|
# @private
|
152
158
|
def left_class
|
153
|
-
|
159
|
+
NoneClass
|
154
160
|
end
|
155
161
|
|
156
162
|
# @private
|
@@ -158,6 +164,29 @@ module Fear
|
|
158
164
|
Some
|
159
165
|
end
|
160
166
|
|
167
|
+
class << self
|
168
|
+
# Build pattern matcher to be used later, despite off
|
169
|
+
# +Option#match+ method, id doesn't apply matcher immanently,
|
170
|
+
# but build it instead. Unusually in sake of efficiency it's better
|
171
|
+
# to statically build matcher and reuse it later.
|
172
|
+
#
|
173
|
+
# @example
|
174
|
+
# matcher =
|
175
|
+
# Option.matcher do |m|
|
176
|
+
# m.some(Integer) { |x| x * 2 }
|
177
|
+
# m.some(String) { |x| x.to_i * 2 }
|
178
|
+
# m.none { 'NaN' }
|
179
|
+
# m.else { 'error '}
|
180
|
+
# end
|
181
|
+
# matcher.call(Some(42))
|
182
|
+
#
|
183
|
+
# @yieldparam [OptionPatternMatch]
|
184
|
+
# @return [Fear::PartialFunction]
|
185
|
+
def matcher(&matcher)
|
186
|
+
OptionPatternMatch.new(&matcher)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
161
190
|
# Include this mixin to access convenient factory methods.
|
162
191
|
# @example
|
163
192
|
# include Fear::Option::Mixin
|
@@ -165,17 +194,22 @@ module Fear
|
|
165
194
|
# Option(17) #=> #<Fear::Some value=17>
|
166
195
|
# Option(nil) #=> #<Fear::None>
|
167
196
|
# Some(17) #=> #<Fear::Some value=17>
|
168
|
-
# None
|
197
|
+
# None #=> #<Fear::None>
|
169
198
|
#
|
170
199
|
module Mixin
|
200
|
+
None = Fear::None
|
201
|
+
|
171
202
|
# An +Option+ factory which creates +Some+ if the argument is
|
172
203
|
# not +nil+, and +None+ if it is +nil+.
|
173
204
|
# @param value [any]
|
174
205
|
# @return [Some, None]
|
175
206
|
#
|
207
|
+
# @example
|
208
|
+
# Option(v)
|
209
|
+
#
|
176
210
|
def Option(value)
|
177
211
|
if value.nil?
|
178
|
-
None
|
212
|
+
None
|
179
213
|
else
|
180
214
|
Some(value)
|
181
215
|
end
|
@@ -183,7 +217,7 @@ module Fear
|
|
183
217
|
|
184
218
|
# @return [None]
|
185
219
|
def None
|
186
|
-
None
|
220
|
+
Fear::None
|
187
221
|
end
|
188
222
|
|
189
223
|
# @param value [any] except nil
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Fear
|
2
|
+
# Option pattern matcher
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# pattern_match =
|
6
|
+
# OptionPatternMatch.new
|
7
|
+
# .some(Integer) { |x| x * 2 }
|
8
|
+
# .some(String) { |x| x.to_i * 2 }
|
9
|
+
# .none { 'NaN' }
|
10
|
+
# .else { 'error '}
|
11
|
+
#
|
12
|
+
# pattern_match.call(42) => 'NaN'
|
13
|
+
#
|
14
|
+
# @example the same matcher may be defined using block syntax
|
15
|
+
# OptionPatternMatch.new do |m|
|
16
|
+
# m.some(Integer) { |x| x * 2 }
|
17
|
+
# m.some(String) { |x| x.to_i * 2 }
|
18
|
+
# m.none { 'NaN' }
|
19
|
+
# m.else { 'error '}
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @note it has two optimized subclasses +Fear::SomePatternMatch+ and +Fear::NonePatternMatch+
|
23
|
+
# @api private
|
24
|
+
class OptionPatternMatch < Fear::PatternMatch
|
25
|
+
GET_METHOD = :get.to_proc
|
26
|
+
|
27
|
+
# Match against Some
|
28
|
+
#
|
29
|
+
# @param conditions [<#==>]
|
30
|
+
# @return [Fear::OptionPatternMatch]
|
31
|
+
def some(*conditions, &effect)
|
32
|
+
branch = Fear.case(Fear::Some, &GET_METHOD).and_then(Fear.case(*conditions, &effect))
|
33
|
+
or_else(branch)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Match against None
|
37
|
+
#
|
38
|
+
# @param effect [Proc]
|
39
|
+
# @return [Fear::OptionPatternMatch]
|
40
|
+
def none(&effect)
|
41
|
+
branch = Fear.case(Fear::None, &effect)
|
42
|
+
or_else(branch)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
# Composite function produced by +PartialFunction#and_then+ method
|
4
|
+
# @api private
|
5
|
+
class AndThen
|
6
|
+
include PartialFunction
|
7
|
+
|
8
|
+
# @param partial_function [Fear::PartialFunction]
|
9
|
+
# @param function [Proc]
|
10
|
+
def initialize(partial_function, &function)
|
11
|
+
@partial_function = partial_function
|
12
|
+
@function = function
|
13
|
+
end
|
14
|
+
# @!attribute partial_function
|
15
|
+
# @return [Fear::PartialFunction]
|
16
|
+
# @!attribute function
|
17
|
+
# @return [Proc]
|
18
|
+
attr_reader :partial_function
|
19
|
+
attr_reader :function
|
20
|
+
private :partial_function
|
21
|
+
private :function
|
22
|
+
|
23
|
+
# @param arg [any]
|
24
|
+
# @return [any ]
|
25
|
+
def call(arg)
|
26
|
+
function.call(partial_function.call(arg))
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param arg [any]
|
30
|
+
# @return [Boolean]
|
31
|
+
def defined_at?(arg)
|
32
|
+
partial_function.defined_at?(arg)
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param arg [any]
|
36
|
+
# @yield [arg]
|
37
|
+
# @return [any]
|
38
|
+
def call_or_else(arg)
|
39
|
+
result = partial_function.call_or_else(arg) do
|
40
|
+
return yield(arg)
|
41
|
+
end
|
42
|
+
function.call(result)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private_constant :AndThen
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
# Any is an object which is always truthy
|
4
|
+
# @api private
|
5
|
+
class Any
|
6
|
+
class << self
|
7
|
+
# @param _other [any]
|
8
|
+
# @return [true]
|
9
|
+
def ===(_other)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param _other [any]
|
14
|
+
# @return [true]
|
15
|
+
def ==(_other)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Proc]
|
20
|
+
def to_proc
|
21
|
+
proc { true }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
# Composite function produced by +PartialFunction#and_then+ method
|
4
|
+
# @api private
|
5
|
+
class Combined
|
6
|
+
include PartialFunction
|
7
|
+
|
8
|
+
# @param f1 [Fear::PartialFunction]
|
9
|
+
# @param f2 [Fear::PartialFunction]
|
10
|
+
def initialize(f1, f2)
|
11
|
+
@f1 = f1
|
12
|
+
@f2 = f2
|
13
|
+
end
|
14
|
+
# @!attribute f1
|
15
|
+
# @return [Fear::PartialFunction]
|
16
|
+
# @!attribute f2
|
17
|
+
# @return [Fear::PartialFunction]
|
18
|
+
attr_reader :f1, :f2
|
19
|
+
private :f1
|
20
|
+
private :f2
|
21
|
+
|
22
|
+
# @param arg [any]
|
23
|
+
# @return [any ]
|
24
|
+
def call(arg)
|
25
|
+
f2.call(f1.call(arg))
|
26
|
+
end
|
27
|
+
|
28
|
+
alias === call
|
29
|
+
alias [] call
|
30
|
+
|
31
|
+
# @param arg [any]
|
32
|
+
# @yieldparam arg [any]
|
33
|
+
# @return [any]
|
34
|
+
def call_or_else(arg)
|
35
|
+
result = f1.call_or_else(arg) { return yield(arg) }
|
36
|
+
f2.call_or_else(result) { |_| return yield(arg) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param arg [any]
|
40
|
+
# @return [Boolean]
|
41
|
+
def defined_at?(arg)
|
42
|
+
result = f1.call_or_else(arg) do
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
f2.defined_at?(result)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private_constant :AndThen
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
class Guard
|
4
|
+
# @api private
|
5
|
+
class And < Guard
|
6
|
+
# @param c1 [Fear::PartialFunction::Guard]
|
7
|
+
# @param c2 [Fear::PartialFunction::Guard]
|
8
|
+
def initialize(c1, c2)
|
9
|
+
@c1 = c1
|
10
|
+
@c2 = c2
|
11
|
+
end
|
12
|
+
attr_reader :c1, :c2
|
13
|
+
private :c1
|
14
|
+
private :c2
|
15
|
+
|
16
|
+
# @param other [Fear::PartialFunction::Guard]
|
17
|
+
# @return [Fear::PartialFunction::Guard]
|
18
|
+
def and(other)
|
19
|
+
Guard::And.new(self, other)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param other [Fear::PartialFunction::Guard]
|
23
|
+
# @return [Fear::PartialFunction::Guard]
|
24
|
+
def or(other)
|
25
|
+
Guard::Or.new(self, other)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param arg [any]
|
29
|
+
# @return [Boolean]
|
30
|
+
def ===(arg)
|
31
|
+
(c1 === arg) && (c2 === arg)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
class Guard
|
4
|
+
# @api private
|
5
|
+
class And3 < Guard
|
6
|
+
# @param c1 [#===]
|
7
|
+
# @param c2 [#===]
|
8
|
+
# @param c3 [#===]
|
9
|
+
def initialize(c1, c2, c3)
|
10
|
+
@c1 = c1
|
11
|
+
@c2 = c2
|
12
|
+
@c3 = c3
|
13
|
+
end
|
14
|
+
attr_reader :c1, :c2, :c3
|
15
|
+
private :c1
|
16
|
+
private :c2
|
17
|
+
private :c3
|
18
|
+
|
19
|
+
# @param other [Fear::PartialFunction::Guard]
|
20
|
+
# @return [Fear::PartialFunction::Guard]
|
21
|
+
def and(other)
|
22
|
+
Guard::And.new(self, other)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param other [Fear::PartialFunction::Guard]
|
26
|
+
# @return [Fear::PartialFunction::Guard]
|
27
|
+
def or(other)
|
28
|
+
Guard::Or.new(self, other)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param arg [any]
|
32
|
+
# @return [Boolean]
|
33
|
+
def ===(arg)
|
34
|
+
(c1 === arg) && (c2 === arg) && (c3 === arg)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
class Guard
|
4
|
+
# @api private
|
5
|
+
class Or < Guard
|
6
|
+
# @param c1 [Fear::PartialFunction::Guard]
|
7
|
+
# @param c2 [Fear::PartialFunction::Guard]
|
8
|
+
def initialize(c1, c2)
|
9
|
+
@c1 = c1
|
10
|
+
@c2 = c2
|
11
|
+
end
|
12
|
+
attr_reader :c1, :c2
|
13
|
+
private :c1
|
14
|
+
private :c2
|
15
|
+
|
16
|
+
# @param other [Fear::PartialFunction::Guard]
|
17
|
+
# @return [Fear::PartialFunction::Guard]
|
18
|
+
def and(other)
|
19
|
+
Guard::And.new(self, other)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param other [Fear::PartialFunction::Guard]
|
23
|
+
# @return [Fear::PartialFunction::Guard]
|
24
|
+
def or(other)
|
25
|
+
Guard::Or.new(self, other)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param arg [any]
|
29
|
+
# @return [Boolean]
|
30
|
+
def ===(arg)
|
31
|
+
(c1 === arg) || (c2 === arg)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
# Guard represents PartialFunction guardian
|
4
|
+
#
|
5
|
+
# @api private
|
6
|
+
class Guard
|
7
|
+
autoload :And, 'fear/partial_function/guard/and'
|
8
|
+
autoload :And3, 'fear/partial_function/guard/and3'
|
9
|
+
autoload :Or, 'fear/partial_function/guard/or'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Optimized version for combination of two guardians
|
13
|
+
# Two guarding is a very common situation. For example checking for Some, and checking
|
14
|
+
# a value withing contianer.
|
15
|
+
#
|
16
|
+
def and2(c1, c2)
|
17
|
+
Guard::And.new(
|
18
|
+
(c1.is_a?(Symbol) ? c1.to_proc : c1),
|
19
|
+
(c2.is_a?(Symbol) ? c2.to_proc : c2),
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def and3(c1, c2, c3)
|
24
|
+
Guard::And3.new(
|
25
|
+
(c1.is_a?(Symbol) ? c1.to_proc : c1),
|
26
|
+
(c2.is_a?(Symbol) ? c2.to_proc : c2),
|
27
|
+
(c3.is_a?(Symbol) ? c3.to_proc : c3),
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def and1(c)
|
32
|
+
c.is_a?(Symbol) ? c.to_proc : c
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param conditions [<#===, Symbol>]
|
36
|
+
# @return [Fear::PartialFunction::Guard]
|
37
|
+
def and(conditions)
|
38
|
+
case conditions.size
|
39
|
+
when 1 then and1(*conditions)
|
40
|
+
when 2 then and2(*conditions)
|
41
|
+
when 3 then and3(*conditions)
|
42
|
+
when 0 then Any
|
43
|
+
else
|
44
|
+
head, *tail = conditions
|
45
|
+
tail.inject(new(head)) { |acc, condition| acc.and(new(condition)) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param conditions [<#===, Symbol>]
|
50
|
+
# @return [Fear::PartialFunction::Guard]
|
51
|
+
def or(conditions)
|
52
|
+
return Any if conditions.empty?
|
53
|
+
|
54
|
+
head, *tail = conditions
|
55
|
+
tail.inject(new(head)) { |acc, condition| acc.or(new(condition)) }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @param condition [<#===, Symbol>]
|
60
|
+
def initialize(condition)
|
61
|
+
@condition =
|
62
|
+
if condition.is_a?(Symbol)
|
63
|
+
condition.to_proc
|
64
|
+
else
|
65
|
+
condition
|
66
|
+
end
|
67
|
+
end
|
68
|
+
attr_reader :condition
|
69
|
+
private :condition
|
70
|
+
|
71
|
+
# @param other [Fear::PartialFunction::Guard]
|
72
|
+
# @return [Fear::PartialFunction::Guard]
|
73
|
+
def and(other)
|
74
|
+
Guard::And.new(condition, other)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param other [Fear::PartialFunction::Guard]
|
78
|
+
# @return [Fear::PartialFunction::Guard]
|
79
|
+
def or(other)
|
80
|
+
Guard::Or.new(condition, other)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param arg [any]
|
84
|
+
# @return [Boolean]
|
85
|
+
def ===(arg)
|
86
|
+
condition === arg
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
# @api private
|
4
|
+
class Lifted
|
5
|
+
# @param pf [Fear::PartialFunction]
|
6
|
+
def initialize(pf)
|
7
|
+
@pf = pf
|
8
|
+
end
|
9
|
+
attr_reader :pf
|
10
|
+
|
11
|
+
# @param arg [any]
|
12
|
+
# @return [Fear::Option]
|
13
|
+
def call(arg)
|
14
|
+
Some.new(pf.call_or_else(arg) { return Fear::None })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private_constant :Lifted
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Fear
|
2
|
+
module PartialFunction
|
3
|
+
# Composite function produced by +PartialFunction#or_else+ method
|
4
|
+
# @api private
|
5
|
+
class OrElse
|
6
|
+
include PartialFunction
|
7
|
+
|
8
|
+
# @param f1 [Fear::PartialFunction]
|
9
|
+
# @param f2 [Fear::PartialFunction]
|
10
|
+
def initialize(f1, f2)
|
11
|
+
@f1 = f1
|
12
|
+
@f2 = f2
|
13
|
+
end
|
14
|
+
# @!attribute f1
|
15
|
+
# @return [Fear::PartialFunction]
|
16
|
+
# @!attribute f2
|
17
|
+
# @return [Fear::PartialFunction]
|
18
|
+
attr_reader :f1, :f2
|
19
|
+
private :f1
|
20
|
+
private :f2
|
21
|
+
|
22
|
+
# @param arg [any]
|
23
|
+
# @return [any]
|
24
|
+
def call(arg)
|
25
|
+
f1.call_or_else(arg, &f2)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias === call
|
29
|
+
alias [] call
|
30
|
+
|
31
|
+
# @param other [Fear::PartialFunction]
|
32
|
+
# @return [Fear::PartialFunction]
|
33
|
+
def or_else(other)
|
34
|
+
OrElse.new(f1, f2.or_else(other))
|
35
|
+
end
|
36
|
+
|
37
|
+
# @see Fear::PartialFunction#and_then
|
38
|
+
def and_then(other = Utils::UNDEFINED, &block)
|
39
|
+
Utils.with_block_or_argument('Fear::PartialFunction::OrElse#and_then', other, block) do |fun|
|
40
|
+
OrElse.new(f1.and_then(&fun), f2.and_then(&fun))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param arg [any]
|
45
|
+
# @return [Boolean]
|
46
|
+
def defined_at?(arg)
|
47
|
+
f1.defined_at?(arg) || f2.defined_at?(arg)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param arg [any]
|
51
|
+
# @param fallback [Proc]
|
52
|
+
# @return [any]
|
53
|
+
def call_or_else(arg, &fallback)
|
54
|
+
f1.call_or_else(arg) do
|
55
|
+
return f2.call_or_else(arg, &fallback)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private_constant :OrElse
|
61
|
+
end
|
62
|
+
end
|