fear 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1fa320f98cb5dc71a7b4e59d7a500c01b729f449
4
- data.tar.gz: ea1935382f1bed41adab1264490f3346ff7d93c9
3
+ metadata.gz: b856c96f41e1cab50482a618c021f32eccd16d97
4
+ data.tar.gz: 7e443f61c506e29e1578f64e7f2237664c1465ec
5
5
  SHA512:
6
- metadata.gz: 49da39785143896e2c6ae73b86765fd7aefdac8603ecbf2bd2d4516cce164dd5946fb9041201f7738eca4fbe8c1e6d78760696a0717d5918c36a7dfe48bfc6ff
7
- data.tar.gz: 66404d368eb7162f626a4f822b4d4cc0975888f764ffa2bbb31354227a04b795ba8ced9f0ee769880aa6831dc90759f24ea87769a39d88795d8485228e053896
6
+ metadata.gz: fef37c2dd16a69752985bd04d734ab27a69513e9db7a114c93dfb66116473d549e3a3106a0221e1f1e61ad0fa1192291cfb9e76eb7fbfc5af7297540d167610c
7
+ data.tar.gz: ff910daab541a9279e7838a971db37d9a37f6bdb220e6b17e985a9e3daaebd61cbba31167efc3e1d7b9201958310270b9788597ecc284b61f0f30bf20586e8f6
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private
data/README.md CHANGED
@@ -32,6 +32,8 @@ The most idiomatic way to use an `Option` instance is to treat it
32
32
  as a collection and use `map`, `flat_map`, `select`, or `each`:
33
33
 
34
34
  ```ruby
35
+ include Fear::Option::Mixin
36
+
35
37
  name = Option(params[:name])
36
38
  upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
37
39
  puts upper.get_or_else('')
@@ -53,6 +55,8 @@ without the need to do explicit exception-handling in all of the places
53
55
  that an exception might occur.
54
56
 
55
57
  ```ruby
58
+ include Fear::Try::Mixin
59
+
56
60
  dividend = Try { Integer(params[:dividend]) }
57
61
  divisor = Try { Integer(params[:divisor]) }
58
62
 
@@ -83,6 +87,8 @@ For example, you could use `Either<String, Fixnum>` to select whether a
83
87
  received input is a `String` or an `Fixnum`.
84
88
 
85
89
  ```ruby
90
+ include Fear::Either::Mixin
91
+
86
92
  input = Readline.readline('Type Either a string or an Int: ', true)
87
93
  result = begin
88
94
  Right(Integer(input))
@@ -107,6 +113,8 @@ It supports two such operations - `flat_map` and `map`. Any class providing them
107
113
  is supported by `For`.
108
114
 
109
115
  ```ruby
116
+ include Fear::For::Mixin
117
+
110
118
  For(a: Some(2), b: Some(3)) do
111
119
  a * b
112
120
  end #=> Some(6)
data/fear.gemspec CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.email = ['abolshakov@spbtv.com']
12
12
  spec.summary = "%q{Ruby port of some Scala's monads.}"
13
13
  spec.description = "Ruby port of some Scala's monads."
14
- spec.homepage = 'https://github.com/bolshakov/functional'
14
+ spec.homepage = 'https://github.com/bolshakov/fear'
15
15
  spec.license = 'MIT'
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0")
data/lib/fear/either.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  module Fear
2
2
  # Represents a value of one of two possible types (a disjoint union.)
3
- # An instance of `Either` is either an instance of `Left` or `Right`.
3
+ # An instance of +Either+ is either an instance of +Left+ or +Right+.
4
4
  #
5
- # A common use of `Either` is as an alternative to `Option` for dealing
6
- # with possible missing values. In this usage, `None` is replaced
7
- # with a `Left` which can contain useful information.
8
- # `Right` takes the place of `Some`. Convention dictates
9
- # that `Left` is used for failure and `Right` is used for success.
5
+ # A common use of +Either+ is as an alternative to +Option+ for dealing
6
+ # with possible missing values. In this usage, +None+ is replaced
7
+ # with a +Left+ which can contain useful information.
8
+ # +Right+ takes the place of +Some+. Convention dictates
9
+ # that +Left+ is used for failure and +Right+ is used for Right.
10
10
  #
11
- # For example, you could use `Either<String, Fixnum>` to select whether a
12
- # received input is a `String` or an `Fixnum`.
11
+ # For example, you could use +Either<String, Fixnum>+ to select_or_else whether a
12
+ # received input is a +String+ or an +Fixnum+.
13
13
  #
14
14
  # @example
15
15
  # in = Readline.readline('Type Either a string or an Int: ', true)
@@ -26,96 +26,198 @@ module Fear
26
26
  # )
27
27
  # )
28
28
  #
29
- # Either is right-biased, which means that `Right` is assumed to be the default case to
30
- # operate on. If it is `Left`, operations like `#map`, `#flat_map`, ... return the `Left` value
29
+ # Either is right-biased, which means that +Right+ is assumed to be the default case to
30
+ # operate on. If it is +Left+, operations like +#map+, +#flat_map+, ... return the +Left+ value
31
31
  # unchanged:
32
32
  #
33
- # @example #map
34
- # Right(12).map { |_| _ * 2) #=> Right(24)
35
- # Left(23).map { |_| _ * 2) #=> Left(23)
36
- #
37
- # @example #get_or_else
38
- # Right(12).get_or_else(17) #=> 12
39
- # Left(12).get_or_else(17) #=> 17
40
- #
41
- # Right(12).get_or_else { 17 } #=> 12
42
- # Left(12).get_or_else { 17 } #=> 17
43
- #
44
- # @example #include?
45
- # # Returns true because value of Right is "something" which equals "something".
46
- # Right("something").include?("something") #= true
47
- #
48
- # # Returns false because value of Right is "something" which does not equal "anything".
49
- # Right("something").include?("anything") #=> false
50
- #
51
- # # Returns false because there is no value for Right.
52
- # Left("something").include?("something") #=> false
53
- #
54
- # @example #each
55
- # Right(12).each { |x| puts x } # prints "12"
56
- # Left(12).each { |x| puts x } # doesn't print
57
- #
58
- # @example #map
59
- # Right('ruby').map(&:length) #=> Right(4)
60
- # Left('ruby').map(&:length) #=> Left('ruby')
61
- #
62
- # @example #flat_map
63
- # Right(12).flat_map { |x| Left('ruby') } #=> Left('ruby')
64
- # Left(12).flat_map { |x| Left('ruby') } #=> Left(12)
65
- #
66
- # @example #select
67
- # Right(12).select(-1, &:even?) #=> Right(12))
68
- # Right(7).select(-1, &:even?) #=> Left(-1)
69
- # Left(12).select(-1, &:even?) #=> Left(-1)
70
- # Left(12).select(-> { -1 }, &:even?) #=> Left(-1)
71
- #
72
- # @example #to_a
73
- # Right(12).to_a #=> [12]
74
- # Left(12).to_a #=> []
75
- #
76
- # @example #to_option
77
- # Right(12).to_option #=> Some(12)
78
- # Left(12).to_option #=> None()
79
- #
80
- # @example #any?
81
- # Right(12).any? { |v| v > 10 } #=> true
82
- # Right(7).any? { |v| v > 10 } #=> false
83
- # Left(12).any? { |v| v > 10 } #=> false
84
- #
85
- # @example #swap
86
- # left = Left("left")
87
- # right = left.swap #=> Right("left")
88
- #
89
- # @example #reduce
90
- # result = possibly_failing_operation()
91
- # log(
92
- # result.reduce(
93
- # ->(ex) { "Operation failed with #{ex}" },
94
- # ->(v) { "Operation produced value: #{v}" },
33
+ # @!method get_or_else(*args)
34
+ # Returns the value from this +Right+ or evaluates the given
35
+ # default argument if this is a +Left+.
36
+ # @overload get_or_else(&default)
37
+ # @yieldreturn [any]
38
+ # @return [any]
39
+ # @example
40
+ # Right(42).get_or_else { 24/2 } #=> 42
41
+ # Left('undefined').get_or_else { 24/2 } #=> 12
42
+ # @overload get_or_else(default)
43
+ # @return [any]
44
+ # @example
45
+ # Right(42).get_or_else(12) #=> 42
46
+ # Left('undefined').get_or_else(12) #=> 12
47
+ #
48
+ # @!method include?(other_value)
49
+ # Returns +true+ if +Right+ has an element that is equal
50
+ # (as determined by +==+) to +other_value+, +false+ otherwise.
51
+ # @param [any]
52
+ # @return [Boolean]
53
+ # @example
54
+ # Right(17).include?(17) #=> true
55
+ # Right(17).include?(7) #=> false
56
+ # Left('undefined').include?(17) #=> false
57
+ #
58
+ # @!method each(&block)
59
+ # Performs the given block if this is a +Right+.
60
+ # @yieldparam [any] value
61
+ # @yieldreturn [void]
62
+ # @return [Option] itself
63
+ # @example
64
+ # Right(17).each do |value|
65
+ # puts value
66
+ # end #=> prints 17
67
+ #
68
+ # Left('undefined').each do |value|
69
+ # puts value
70
+ # end #=> does nothing
71
+ #
72
+ # @!method map(&block)
73
+ # Maps the given block to the value from this +Right+ or
74
+ # returns this if this is a +Left+.
75
+ # @yieldparam [any] value
76
+ # @yieldreturn [any]
77
+ # @example
78
+ # Right(42).map { |v| v/2 } #=> Right(21)
79
+ # Left('undefined').map { |v| v/2 } #=> Left('undefined')
80
+ #
81
+ # @!method flat_map(&block)
82
+ # Returns the given block applied to the value from this +Right+
83
+ # or returns this if this is a +Left+.
84
+ # @yieldparam [any] value
85
+ # @yieldreturn [Option]
86
+ # @return [Option]
87
+ # @example
88
+ # Right(42).flat_map { |v| Right(v/2) } #=> Right(21)
89
+ # Left('undefined').flat_map { |v| Right(v/2) } #=> Left('undefined')
90
+ #
91
+ # @!method to_a
92
+ # Returns an +Array+ containing the +Right+ value or an
93
+ # empty +Array+ if this is a +Left+.
94
+ # @return [Array]
95
+ # @example
96
+ # Right(42).to_a #=> [21]
97
+ # Left('undefined').to_a #=> []
98
+ #
99
+ # @!method to_option
100
+ # Returns an +Some+ containing the +Right+ value or a +None+ if
101
+ # this is a +Left+.
102
+ # @return [Option]
103
+ # @example
104
+ # Right(42).to_option #=> Some(21)
105
+ # Left('undefined').to_option #=> None()
106
+ #
107
+ # @!method any?(&predicate)
108
+ # Returns +false+ if +Left+ or returns the result of the
109
+ # application of the given predicate to the +Right+ value.
110
+ # @yieldparam [any] value
111
+ # @yieldreturn [Boolean]
112
+ # @return [Boolean]
113
+ # @example
114
+ # Right(12).any?( |v| v > 10) #=> true
115
+ # Right(7).any?( |v| v > 10) #=> false
116
+ # Left('undefined').any?( |v| v > 10) #=> false
117
+ #
118
+ # -----
119
+ #
120
+ # @!method right?
121
+ # Returns +true+ if this is a +Right+, +false+ otherwise.
122
+ # @note this method is also aliased as +#success?+
123
+ # @return [Boolean]
124
+ # @example
125
+ # Right(42).right? #=> true
126
+ # Left('err').right? #=> false
127
+ #
128
+ # @!method left?
129
+ # Returns +true+ if this is a +Left+, +false+ otherwise.
130
+ # @note this method is also aliased as +#failure?+
131
+ # @return [Boolean]
132
+ # @example
133
+ # Right(42).left? #=> false
134
+ # Left('err').left? #=> true
135
+ #
136
+ # @!method select_or_else(default, &predicate)
137
+ # Returns +Left+ of the default if the given predicate
138
+ # does not hold for the right value, otherwise, returns +Right+.
139
+ # @param default [Object, Proc]
140
+ # @yieldparam value [Object]
141
+ # @yieldreturn [Boolean]
142
+ # @return [Either]
143
+ # @example
144
+ # Right(12).select_or_else(-1, &:even?) #=> Right(12)
145
+ # Right(7).select_or_else(-1, &:even?) #=> Left(-1)
146
+ # Left(12).select_or_else(-1, &:even?) #=> Left(-1)
147
+ # Left(12).select_or_else(-> { -1 }, &:even?) #=> Left(-1)
148
+ #
149
+ # @!method select(&predicate)
150
+ # Returns +Left+ of value if the given predicate
151
+ # does not hold for the right value, otherwise, returns +Right+.
152
+ # @yieldparam value [Object]
153
+ # @yieldreturn [Boolean]
154
+ # @return [Either]
155
+ # @example
156
+ # Right(12).select(&:even?) #=> Right(12)
157
+ # Right(7).select(&:even?) #=> Left(7)
158
+ # Left(12).select(&:even?) #=> Left(12)
159
+ # Left(7).select(&:even?) #=> Left(7)
160
+ #
161
+ # @!method swap
162
+ # If this is a +Left+, then return the left value in +Right+ or vice versa.
163
+ # @return [Either]
164
+ # @example
165
+ # Left('left').swap #=> Right('left')
166
+ # Right('right').swap #=> Light('left')
167
+ #
168
+ # @!method reduce(reduce_left, reduce_right)
169
+ # Applies +reduce_left+ if this is a +Left+ or +reduce_right+ if
170
+ # this is a +Right+.
171
+ # @param reduce_left [Proc] the Proc to apply if this is a +Left+
172
+ # @param reduce_right [Proc] the Proc to apply if this is a +Right+
173
+ # @return [any] the results of applying the Proc
174
+ # @example
175
+ # result = possibly_failing_operation()
176
+ # log(
177
+ # result.reduce(
178
+ # ->(ex) { "Operation failed with #{ex}" },
179
+ # ->(v) { "Operation produced value: #{v}" },
180
+ # )
95
181
  # )
96
- # )
97
182
  #
98
- # @example #join_right
99
- # Right(Right(12)).join_right #=> Right(12)
100
- # Right(Left("flower")).join_right #=> Left("flower")
101
- # Left("flower").join_right #=> Left("flower")
102
- # Left(Right("flower")).join_right #=> Left(Right("flower"))
103
183
  #
104
- # @example #join_left
105
- # Left(Right("flower")).join_left #=> Right("flower")
106
- # Left(Left(12)).join_left #=> Left(12)
107
- # Right("daisy").join_left #=> Right("daisy")
108
- # Right(Left("daisy")).join_left #=> Right(Left("daisy"))
184
+ # @!method join_right
185
+ # Joins an +Either+ through +Right+. This method requires
186
+ # that the right side of this +Either+ is itself an
187
+ # +Either+ type.
188
+ # @note This method, and +join_left+, are analogous to +Option#flatten+
189
+ # @return [Either]
190
+ # @raise [TypeError] if it does not contain +Either+.
191
+ # @example
192
+ # Right(Right(12)).join_right #=> Right(12)
193
+ # Right(Left("flower")).join_right #=> Left("flower")
194
+ # Left("flower").join_right #=> Left("flower")
195
+ # Left(Right("flower")).join_right #=> Left(Right("flower"))
196
+ #
197
+ # # @!method join_right
198
+ # Joins an +Either+ through +Left+. This method requires
199
+ # that the left side of this +Either+ is itself an
200
+ # +Either+ type.
201
+ # @note This method, and +join_right+, are analogous to +Option#flatten+
202
+ # @return [Either]
203
+ # @raise [TypeError] if it does not contain +Either+.
204
+ # @example
205
+ # Left(Right("flower")).join_left #=> Right("flower")
206
+ # Left(Left(12)).join_left #=> Left(12)
207
+ # Right("daisy").join_left #=> Right("daisy")
208
+ # Right(Left("daisy")).join_left #=> Right(Left("daisy"))
109
209
  #
110
210
  # @see https://github.com/scala/scala/blob/2.12.x/src/library/scala/util/Either.scala
111
211
  #
112
212
  module Either
113
213
  include Dry::Equalizer(:value)
114
214
 
215
+ # @private
115
216
  def left_class
116
217
  Left
117
218
  end
118
219
 
220
+ # @private
119
221
  def right_class
120
222
  Right
121
223
  end
@@ -124,23 +226,16 @@ module Fear
124
226
  @value = value
125
227
  end
126
228
 
127
- # @return [Boolean]
128
- def right?
129
- is_a?(Right)
130
- end
131
-
132
- alias success? right?
133
-
134
- # @return [Boolean]
135
- def left?
136
- !right?
137
- end
138
-
139
- alias failure? left?
140
-
141
229
  attr_reader :value
142
230
  protected :value
143
231
 
232
+ # Include this mixin to access convenient factory methods.
233
+ # @example
234
+ # include Fear::Either::Mixin
235
+ #
236
+ # Right('flower') #=> #<Fear::Right value='flower'>
237
+ # Left('beaf') #=> #<Fear::Legt value='beaf'>
238
+ #
144
239
  module Mixin
145
240
  # @param [any]
146
241
  # @return [Left]
data/lib/fear/failure.rb CHANGED
@@ -4,6 +4,7 @@ module Fear
4
4
  include Dry::Equalizer(:value)
5
5
  include RightBiased::Left
6
6
 
7
+ # @param [StandardError]
7
8
  def initialize(exception)
8
9
  @value = exception
9
10
  end
@@ -11,10 +12,17 @@ module Fear
11
12
  attr_reader :value
12
13
  protected :value
13
14
 
15
+ # @return [Boolean]
14
16
  def success?
15
17
  false
16
18
  end
17
19
 
20
+ # @return [true]
21
+ def failure?
22
+ true
23
+ end
24
+
25
+ # @raise
18
26
  def get
19
27
  fail value
20
28
  end
@@ -36,13 +44,9 @@ module Fear
36
44
  self
37
45
  end
38
46
 
39
- # Applies the given block to exception.
40
- # This is like `flat_map` for the exception.
41
- #
42
47
  # @yieldparam [Exception]
43
48
  # @yieldreturn [Try]
44
49
  # @return [Try]
45
- #
46
50
  def recover_with
47
51
  yield(value).tap do |result|
48
52
  Utils.assert_type!(result, Success, Failure)
@@ -51,14 +55,18 @@ module Fear
51
55
  Failure.new(error)
52
56
  end
53
57
 
54
- # Applies the given block to exception.
55
- # This is like map for the exception.
58
+ # @yieldparam [Exception]
59
+ # @yieldreturn [any]
56
60
  # @return [Try]
57
- #
58
61
  def recover
59
62
  Success.new(yield(value))
60
63
  rescue => error
61
64
  Failure.new(error)
62
65
  end
66
+
67
+ # @return [Left]
68
+ def to_either
69
+ Left.new(value)
70
+ end
63
71
  end
64
72
  end
data/lib/fear/for.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  module Fear
2
2
  # This class provides syntactic sugar for composition of
3
3
  # multiple monadic operations. It supports two such
4
- # operations - `flat_map` and `map`. Any class providing them
5
- # is supported by `For`.
4
+ # operations - +flat_map+ and +map+. Any class providing them
5
+ # is supported by +For+.
6
6
  #
7
- # @example Option
8
- # For(a: Some(2), b: Some(3)) { a * b } #=> Some(6)
9
- # # If one of operands is None, the result is None
10
- # For(a: Some(2), b: None()) { a * b } #=> None()
11
- # For(a: None(), b: Some(2)) { a * b } #=> None()
7
+ # For(a: Some(2), b: Some(3)) { a * b } #=> Some(6)
8
+ #
9
+ # If one of operands is None, the result is None
10
+ #
11
+ # For(a: Some(2), b: None()) { a * b } #=> None()
12
+ # For(a: None(), b: Some(2)) { a * b } #=> None()
12
13
  #
13
14
  # Lets look at first example:
14
15
  #
@@ -71,6 +72,7 @@ module Fear
71
72
  @variables = variables
72
73
  end
73
74
  attr_reader :variables
75
+ private :variables
74
76
 
75
77
  def call(&block)
76
78
  variable_name_and_monad, *tail = *variables
@@ -92,6 +94,19 @@ module Fear
92
94
  end
93
95
  end
94
96
 
97
+ # Include this mixin to access convenient factory method for +For+.
98
+ # @example
99
+ # include Fear::For::Mixin
100
+ #
101
+ # For(a: Some(2), b: Some(3)) { a * b } #=> Some(6)
102
+ # For(a: Some(2), b: None()) { a * b } #=> None()
103
+ #
104
+ # For(a: Right(2), b: Right(3)) { a * b } #=> Right(6)
105
+ # For(a: Right(2), b: Left(3)) { a * b } #=> Left(3)
106
+ #
107
+ # For(a: Success(2), b: Success(3)) { a * b } #=> Success(3)
108
+ # For(a: Success(2), b: Failure(...)) { a * b } #=> Failure(...)
109
+ #
95
110
  module Mixin
96
111
  # @param locals [Hash{Symbol => {#map, #flat_map}}]
97
112
  # @return [{#map, #flat_map}]
data/lib/fear/left.rb CHANGED
@@ -3,46 +3,47 @@ module Fear
3
3
  include Either
4
4
  include RightBiased::Left
5
5
 
6
- # Returns `Left` or `default`.
7
- #
6
+ # @return [false]
7
+ def right?
8
+ false
9
+ end
10
+ alias success? right?
11
+
12
+ # @return [true]
13
+ def left?
14
+ true
15
+ end
16
+ alias failure? left?
17
+
8
18
  # @param default [Proc, any]
9
19
  # @return [Either]
10
- #
11
- def select(default)
20
+ def select_or_else(default)
12
21
  Left.new(Utils.return_or_call_proc(default))
13
22
  end
14
23
 
24
+ # @return [Left]
25
+ def select
26
+ self
27
+ end
28
+
15
29
  # @return [Right] value in `Right`
16
- #
17
30
  def swap
18
31
  Right.new(value)
19
32
  end
20
33
 
21
- # @param reduce_left [Proc] the function to apply if this is a `Left`
22
- # @return [any] Applies `reduce_left` to the value.
23
- #
34
+ # @param reduce_left [Proc]
35
+ # @return [any]
24
36
  def reduce(reduce_left, _)
25
37
  reduce_left.call(value)
26
38
  end
27
39
 
28
- # Joins an `Either` through `Right`.
29
- #
30
40
  # @return [self]
31
- #
32
41
  def join_right
33
42
  self
34
43
  end
35
44
 
36
- # Joins an `Either` through `Left`.
37
- #
38
- # This method requires that the left side of this `Either` is itself an
39
- # Either type.
40
- #
41
- # This method, and `join_right`, are analogous to `Option#flatten`
42
- #
43
45
  # @return [Either]
44
- # @raise [TypeError] if it does not contain `Either`.
45
- #
46
+ # @raise [TypeError]
46
47
  def join_left
47
48
  value.tap do |v|
48
49
  Utils.assert_type!(v, Either)
data/lib/fear/none.rb CHANGED
@@ -4,18 +4,27 @@ module Fear
4
4
  include Dry::Equalizer()
5
5
  include RightBiased::Left
6
6
 
7
- # Ignores the given block and return self.
8
- #
7
+ # @raise [NoSuchElementError]
8
+ def get
9
+ fail NoSuchElementError
10
+ end
11
+
12
+ # @return [nil]
13
+ def or_nil
14
+ nil
15
+ end
16
+
17
+ # @return [true]
18
+ def empty?
19
+ true
20
+ end
21
+
9
22
  # @return [None]
10
- #
11
23
  def select(*)
12
24
  self
13
25
  end
14
26
 
15
- # Ignores the given block and return self.
16
- #
17
27
  # @return [None]
18
- #
19
28
  def reject(*)
20
29
  self
21
30
  end