dry-monads 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +157 -73
  3. data/LICENSE +1 -1
  4. data/README.md +18 -38
  5. data/dry-monads.gemspec +32 -30
  6. data/lib/dry-monads.rb +3 -1
  7. data/lib/dry/monads.rb +4 -2
  8. data/lib/dry/monads/all.rb +4 -2
  9. data/lib/dry/monads/constants.rb +1 -1
  10. data/lib/dry/monads/conversion_stubs.rb +2 -0
  11. data/lib/dry/monads/curry.rb +2 -0
  12. data/lib/dry/monads/do.rb +55 -17
  13. data/lib/dry/monads/do/all.rb +39 -17
  14. data/lib/dry/monads/do/mixin.rb +2 -0
  15. data/lib/dry/monads/either.rb +9 -7
  16. data/lib/dry/monads/errors.rb +8 -3
  17. data/lib/dry/monads/lazy.rb +19 -6
  18. data/lib/dry/monads/list.rb +31 -30
  19. data/lib/dry/monads/maybe.rb +90 -19
  20. data/lib/dry/monads/registry.rb +15 -12
  21. data/lib/dry/monads/result.rb +42 -15
  22. data/lib/dry/monads/result/fixed.rb +35 -24
  23. data/lib/dry/monads/right_biased.rb +45 -24
  24. data/lib/dry/monads/task.rb +25 -22
  25. data/lib/dry/monads/transformer.rb +4 -1
  26. data/lib/dry/monads/traverse.rb +9 -1
  27. data/lib/dry/monads/try.rb +51 -13
  28. data/lib/dry/monads/unit.rb +6 -2
  29. data/lib/dry/monads/validated.rb +27 -20
  30. data/lib/dry/monads/version.rb +3 -1
  31. data/lib/json/add/dry/monads/maybe.rb +4 -3
  32. metadata +27 -75
  33. data/.codeclimate.yml +0 -12
  34. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  35. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
  36. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  37. data/.github/workflows/ci.yml +0 -74
  38. data/.github/workflows/docsite.yml +0 -34
  39. data/.github/workflows/sync_configs.yml +0 -34
  40. data/.gitignore +0 -10
  41. data/.rspec +0 -4
  42. data/.rubocop.yml +0 -89
  43. data/.yardopts +0 -4
  44. data/CODE_OF_CONDUCT.md +0 -13
  45. data/CONTRIBUTING.md +0 -29
  46. data/Gemfile +0 -23
  47. data/Rakefile +0 -6
  48. data/bin/console +0 -16
  49. data/bin/setup +0 -7
  50. data/docsite/source/case-equality.html.md +0 -42
  51. data/docsite/source/do-notation.html.md +0 -207
  52. data/docsite/source/getting-started.html.md +0 -142
  53. data/docsite/source/index.html.md +0 -179
  54. data/docsite/source/list.html.md +0 -87
  55. data/docsite/source/maybe.html.md +0 -146
  56. data/docsite/source/pattern-matching.html.md +0 -68
  57. data/docsite/source/result.html.md +0 -190
  58. data/docsite/source/task.html.md +0 -126
  59. data/docsite/source/tracing-failures.html.md +0 -32
  60. data/docsite/source/try.html.md +0 -76
  61. data/docsite/source/unit.html.md +0 -36
  62. data/docsite/source/validated.html.md +0 -88
  63. data/log/.gitkeep +0 -0
@@ -1,10 +1,12 @@
1
- require 'dry/equalizer'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/constants'
4
- require 'dry/monads/right_biased'
5
- require 'dry/monads/transformer'
6
- require 'dry/monads/conversion_stubs'
7
- require 'dry/monads/unit'
3
+ require "dry/core/equalizer"
4
+
5
+ require "dry/monads/constants"
6
+ require "dry/monads/right_biased"
7
+ require "dry/monads/transformer"
8
+ require "dry/monads/conversion_stubs"
9
+ require "dry/monads/unit"
8
10
 
9
11
  module Dry
10
12
  module Monads
@@ -84,6 +86,7 @@ module Dry
84
86
 
85
87
  # @param value [Object] a value of a successful operation
86
88
  def initialize(value)
89
+ super()
87
90
  @value = value
88
91
  end
89
92
 
@@ -132,7 +135,7 @@ module Dry
132
135
  # @return [String]
133
136
  def to_s
134
137
  if Unit.equal?(@value)
135
- 'Success()'
138
+ "Success()"
136
139
  else
137
140
  "Success(#{@value.inspect})"
138
141
  end
@@ -145,6 +148,13 @@ module Dry
145
148
  def flip
146
149
  Failure.new(@value, RightBiased::Left.trace_caller)
147
150
  end
151
+
152
+ # Ignores values and returns self, see {Failure#alt_map}
153
+ #
154
+ # @return [Result::Success]
155
+ def alt_map(_ = nil)
156
+ self
157
+ end
148
158
  end
149
159
 
150
160
  # Represents a value of a failed operation.
@@ -186,6 +196,7 @@ module Dry
186
196
  # @param value [Object] failure value
187
197
  # @param trace [String] caller line
188
198
  def initialize(value, trace = RightBiased::Left.trace_caller)
199
+ super()
189
200
  @value = value
190
201
  @trace = trace
191
202
  end
@@ -216,7 +227,8 @@ module Dry
216
227
  # otherwise simply returns the first argument.
217
228
  #
218
229
  # @example
219
- # Dry::Monads.Failure(ArgumentError.new('error message')).or(&:message) # => "error message"
230
+ # Dry::Monads.Failure(ArgumentError.new('error message')).or(&:message)
231
+ # # => "error message"
220
232
  #
221
233
  # @param args [Array<Object>] arguments that will be passed to a block
222
234
  # if one was given, otherwise the first
@@ -230,7 +242,8 @@ module Dry
230
242
  end
231
243
  end
232
244
 
233
- # A lifted version of `#or`. Wraps the passed value or the block result with Result::Success.
245
+ # A lifted version of `#or`. Wraps the passed value or the block
246
+ # result with Result::Success.
234
247
  #
235
248
  # @example
236
249
  # Dry::Monads.Failure.new('no value').or_fmap('value') # => Success("value")
@@ -245,7 +258,7 @@ module Dry
245
258
  # @return [String]
246
259
  def to_s
247
260
  if Unit.equal?(@value)
248
- 'Failure()'
261
+ "Failure()"
249
262
  else
250
263
  "Failure(#{@value.inspect})"
251
264
  end
@@ -285,6 +298,21 @@ module Dry
285
298
  def either(_, g)
286
299
  g.(failure)
287
300
  end
301
+
302
+ # Lifts a block/proc over Failure
303
+ #
304
+ # @overload alt_map(proc)
305
+ # @param proc [#call]
306
+ # @return [Result::Failure]
307
+ #
308
+ # @overload alt_map
309
+ # @param block [Proc]
310
+ # @return [Result::Failure]
311
+ #
312
+ def alt_map(proc = Undefined, &block)
313
+ f = Undefined.default(proc, block)
314
+ self.class.new(f.(failure), RightBiased::Left.trace_caller)
315
+ end
288
316
  end
289
317
 
290
318
  # A module that can be included for easier access to Result monads.
@@ -299,7 +327,6 @@ module Dry
299
327
  # Value constructors
300
328
  #
301
329
  module Constructors
302
-
303
330
  # Success constructor
304
331
  #
305
332
  # @overload Success(value)
@@ -386,7 +413,7 @@ module Dry
386
413
  # @param fail [#call] Fallback value
387
414
  # @param block [Proc] Fallback block
388
415
  # @return [Success<Any>]
389
- def to_result(fail = Unit, &block)
416
+ def to_result(_fail = Unit)
390
417
  Result::Success.new(@value)
391
418
  end
392
419
  end
@@ -397,7 +424,7 @@ module Dry
397
424
  # @param fail [#call] Fallback value
398
425
  # @param block [Proc] Fallback block
399
426
  # @return [Failure<Any>]
400
- def to_result(fail = Unit, &block)
427
+ def to_result(fail = Unit)
401
428
  if block_given?
402
429
  Result::Failure.new(yield)
403
430
  else
@@ -447,7 +474,7 @@ module Dry
447
474
  end
448
475
 
449
476
  class Invalid < Validated
450
- # Concerts to Result::Failure
477
+ # Converts to Result::Failure
451
478
  #
452
479
  # @return [Result::Failure]
453
480
  def to_result
@@ -456,7 +483,7 @@ module Dry
456
483
  end
457
484
  end
458
485
 
459
- require 'dry/monads/registry'
486
+ require "dry/monads/registry"
460
487
  register_mixin(:result, Result::Mixin)
461
488
  end
462
489
  end
@@ -1,35 +1,46 @@
1
- module Dry::Monads
2
- class Result
3
- # @see Monads#Result
4
- # @private
5
- class Fixed < Module
6
- def self.[](error, **options)
7
- new(error, **options)
8
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/monads/constants"
4
+
5
+ module Dry
6
+ module Monads
7
+ class Result
8
+ # @see Monads#Result
9
+ # @private
10
+ class Fixed < ::Module
11
+ def self.[](error, **options)
12
+ new(error, **options)
13
+ end
14
+
15
+ def initialize(error, **_options)
16
+ super()
9
17
 
10
- def initialize(error, **options)
11
- @mod = Module.new do
12
- define_method(:Failure) do |value|
13
- if error === value
14
- Failure.new(value, RightBiased::Left.trace_caller)
15
- else
16
- raise InvalidFailureTypeError.new(value)
18
+ @mod = ::Module.new do
19
+ define_method(:Failure) do |value|
20
+ if error === value
21
+ Failure.new(value, RightBiased::Left.trace_caller)
22
+ else
23
+ # rubocop:disable Style/RaiseArgs
24
+ # per https://github.com/dry-rb/dry-monads/pull/142
25
+ raise InvalidFailureTypeError.new(value)
26
+ # rubocop:enable Style/RaiseArgs
27
+ end
17
28
  end
18
- end
19
29
 
20
- def Success(value = Undefined, &block)
21
- v = Undefined.default(value, block || Unit)
22
- Success.new(v)
30
+ def Success(value = Undefined, &block)
31
+ v = Undefined.default(value, block || Unit)
32
+ Success.new(v)
33
+ end
23
34
  end
24
35
  end
25
- end
26
36
 
27
- private
37
+ private
28
38
 
29
- def included(base)
30
- super
39
+ def included(base)
40
+ super
31
41
 
32
- base.include(@mod)
42
+ base.include(@mod)
43
+ end
33
44
  end
34
45
  end
35
46
  end
@@ -1,7 +1,9 @@
1
- require 'dry/monads/constants'
2
- require 'dry/monads/unit'
3
- require 'dry/monads/curry'
4
- require 'dry/monads/errors'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/monads/constants"
4
+ require "dry/monads/unit"
5
+ require "dry/monads/curry"
6
+ require "dry/monads/errors"
5
7
 
6
8
  module Dry
7
9
  module Monads
@@ -10,7 +12,7 @@ module Dry
10
12
  # Right part
11
13
  #
12
14
  # @api public
13
- module Right
15
+ module Right # rubocop:disable Metrics/ModuleLength
14
16
  # @private
15
17
  def self.included(m)
16
18
  super
@@ -88,6 +90,16 @@ module Dry
88
90
  self
89
91
  end
90
92
 
93
+ # Ignores arguments and returns self. It exists to keep the interface
94
+ # identical to that of {RightBiased::Left}.
95
+ #
96
+ # @param _alt [RightBiased::Right, RightBiased::Left]
97
+ #
98
+ # @return [RightBiased::Right]
99
+ def |(_alt)
100
+ self
101
+ end
102
+
91
103
  # A lifted version of `#or`. For {RightBiased::Right} acts in the same way as `#or`,
92
104
  # that is returns itselt.
93
105
  #
@@ -116,18 +128,18 @@ module Dry
116
128
  # create_user.apply(name) # => Failure(:name_missing)
117
129
  #
118
130
  # @return [RightBiased::Left,RightBiased::Right]
119
- def apply(val = Undefined)
131
+ def apply(val = Undefined, &block)
120
132
  unless @value.respond_to?(:call)
121
- raise TypeError, "Cannot apply #{ val.inspect } to #{ @value.inspect }"
133
+ raise TypeError, "Cannot apply #{val.inspect} to #{@value.inspect}"
122
134
  end
123
135
 
124
- Undefined.default(val) { yield }.fmap { |unwrapped| curry.(unwrapped) }
136
+ Undefined.default(val, &block).fmap { |unwrapped| curry.(unwrapped) }
125
137
  end
126
138
 
127
139
  # @param other [Object]
128
140
  # @return [Boolean]
129
141
  def ===(other)
130
- self.class == other.class && value! === other.value!
142
+ other.instance_of?(self.class) && value! === other.value!
131
143
  end
132
144
 
133
145
  # Maps the value to Dry::Monads::Unit, useful when you don't care
@@ -195,11 +207,11 @@ module Dry
195
207
  # @api private
196
208
  def deconstruct
197
209
  if Unit.equal?(@value)
198
- []
199
- elsif @value.is_a?(::Array)
200
- @value
201
- else
210
+ EMPTY_ARRAY
211
+ elsif !@value.is_a?(::Array)
202
212
  [@value]
213
+ else
214
+ @value
203
215
  end
204
216
  end
205
217
 
@@ -214,9 +226,9 @@ module Dry
214
226
  # end
215
227
  #
216
228
  # @api private
217
- def deconstruct_keys(_)
218
- if @value.is_a?(::Hash)
219
- @value
229
+ def deconstruct_keys(keys)
230
+ if @value.respond_to?(:deconstruct_keys)
231
+ @value.deconstruct_keys(keys)
220
232
  else
221
233
  EMPTY_HASH
222
234
  end
@@ -224,7 +236,7 @@ module Dry
224
236
 
225
237
  private
226
238
 
227
- if RUBY_VERSION >= '2.7'
239
+ if RUBY_VERSION >= "2.7"
228
240
  # @api private
229
241
  def destructure(value)
230
242
  if value.is_a?(::Hash)
@@ -253,12 +265,12 @@ module Dry
253
265
  # @private
254
266
  # @return [String] Caller location
255
267
  def self.trace_caller
256
- caller_locations(2, 2)[0].to_s
268
+ caller_locations(2, 1)[0].to_s
257
269
  end
258
270
 
259
271
  # Raises an error on accessing internal value
260
272
  def value!
261
- raise UnwrapError.new(self)
273
+ raise UnwrapError, self
262
274
  end
263
275
 
264
276
  # Ignores the input parameter and returns self. It exists to keep the interface
@@ -297,11 +309,20 @@ module Dry
297
309
  raise NotImplementedError
298
310
  end
299
311
 
312
+ # Returns the passed value. Works in pair with {RightBiased::Right#|}.
313
+ #
314
+ # @param alt [RightBiased::Right, RightBiased::Left]
315
+ #
316
+ # @return [RightBiased::Right, RightBiased::Left]
317
+ def |(alt)
318
+ self.or(alt)
319
+ end
320
+
300
321
  # A lifted version of `#or`. This is basically `#or` + `#fmap`.
301
322
  #
302
323
  # @example
303
- # Dry::Monads.None.or('no value') # => Some("no value")
304
- # Dry::Monads.None.or { Time.now } # => Some(current time)
324
+ # Dry::Monads.None.or_fmap('no value') # => Some("no value")
325
+ # Dry::Monads.None.or_fmap { Time.now } # => Some(current time)
305
326
  #
306
327
  # @return [RightBiased::Left, RightBiased::Right]
307
328
  def or_fmap(*)
@@ -381,9 +402,9 @@ module Dry
381
402
  # end
382
403
  #
383
404
  # @api private
384
- def deconstruct_keys(_)
385
- if @value.is_a?(::Hash)
386
- @value
405
+ def deconstruct_keys(keys)
406
+ if @value.respond_to?(:deconstruct_keys)
407
+ @value.deconstruct_keys(keys)
387
408
  else
388
409
  EMPTY_HASH
389
410
  end
@@ -1,8 +1,10 @@
1
- require 'concurrent/promise'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'dry/monads/unit'
4
- require 'dry/monads/curry'
5
- require 'dry/monads/conversion_stubs'
3
+ require "concurrent/promise"
4
+
5
+ require "dry/monads/unit"
6
+ require "dry/monads/curry"
7
+ require "dry/monads/conversion_stubs"
6
8
 
7
9
  module Dry
8
10
  module Monads
@@ -46,9 +48,11 @@ module Dry
46
48
  # @example using a predefined executor
47
49
  # Task[:fast] { do_quick_task }
48
50
  #
49
- # @param executor [Concurrent::AbstractExecutorService,Symbol] Either an executor instance
50
- # or a name of predefined global
51
- # from concurrent-ruby
51
+ # @param executor [Concurrent::AbstractExecutorService,Symbol]
52
+ # Either an executor instance
53
+ # or a name of predefined global
54
+ # from concurrent-ruby
55
+ #
52
56
  # @return [Task]
53
57
  def [](executor, &block)
54
58
  new(Promise.execute(executor: executor, &block))
@@ -129,14 +133,14 @@ module Dry
129
133
  state = case promise.state
130
134
  when :fulfilled
131
135
  if Unit.equal?(value!)
132
- 'value=()'
136
+ "value=()"
133
137
  else
134
138
  "value=#{value!.inspect}"
135
139
  end
136
140
  when :rejected
137
- "error=#{ promise.reason.inspect }"
141
+ "error=#{promise.reason.inspect}"
138
142
  else
139
- '?'
143
+ "?"
140
144
  end
141
145
 
142
146
  "Task(#{state})"
@@ -162,16 +166,14 @@ module Dry
162
166
  )
163
167
 
164
168
  promise.on_error do |v|
165
- begin
166
- inner = block.(v).promise
167
- inner.execute
168
- inner.on_success { |r| child.on_fulfill(r) }
169
- inner.on_error { |e| child.on_reject(e) }
170
- rescue => e
171
- child.on_reject(e)
172
- end
169
+ inner = block.(v).promise
170
+ inner.execute
171
+ inner.on_success { |r| child.on_fulfill(r) }
172
+ inner.on_error { |e| child.on_reject(e) }
173
+ rescue StandardError => e
174
+ child.on_reject(e)
173
175
  end
174
- promise.on_success { |v| child.on_fulfill(v) }
176
+ promise.on_success { |v| child.on_fulfill(v) }
175
177
 
176
178
  self.class.new(child)
177
179
  end
@@ -200,6 +202,7 @@ module Dry
200
202
  def ==(other)
201
203
  return true if equal?(other)
202
204
  return false unless self.class == other.class
205
+
203
206
  compare_promises(promise, other.promise)
204
207
  end
205
208
 
@@ -233,8 +236,8 @@ module Dry
233
236
  #
234
237
  # @param val [Task]
235
238
  # @return [Task]
236
- def apply(val = Undefined)
237
- arg = Undefined.default(val) { yield }
239
+ def apply(val = Undefined, &block)
240
+ arg = Undefined.default(val, &block)
238
241
  bind { |f| arg.fmap { |v| curry(f).(v) } }
239
242
  end
240
243
 
@@ -311,7 +314,7 @@ module Dry
311
314
  end
312
315
  end
313
316
 
314
- require 'dry/monads/registry'
317
+ require "dry/monads/registry"
315
318
  register_mixin(:task, Task::Mixin)
316
319
  end
317
320
  end