dry-monads 1.3.2 → 1.4.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.
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