fear 0.1.0 → 0.2.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 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