fear 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|