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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +157 -73
- data/LICENSE +1 -1
- data/README.md +18 -38
- data/dry-monads.gemspec +32 -30
- data/lib/dry-monads.rb +3 -1
- data/lib/dry/monads.rb +4 -2
- data/lib/dry/monads/all.rb +4 -2
- data/lib/dry/monads/constants.rb +1 -1
- data/lib/dry/monads/conversion_stubs.rb +2 -0
- data/lib/dry/monads/curry.rb +2 -0
- data/lib/dry/monads/do.rb +55 -17
- data/lib/dry/monads/do/all.rb +39 -17
- data/lib/dry/monads/do/mixin.rb +2 -0
- data/lib/dry/monads/either.rb +9 -7
- data/lib/dry/monads/errors.rb +8 -3
- data/lib/dry/monads/lazy.rb +19 -6
- data/lib/dry/monads/list.rb +31 -30
- data/lib/dry/monads/maybe.rb +90 -19
- data/lib/dry/monads/registry.rb +15 -12
- data/lib/dry/monads/result.rb +42 -15
- data/lib/dry/monads/result/fixed.rb +35 -24
- data/lib/dry/monads/right_biased.rb +45 -24
- data/lib/dry/monads/task.rb +25 -22
- data/lib/dry/monads/transformer.rb +4 -1
- data/lib/dry/monads/traverse.rb +9 -1
- data/lib/dry/monads/try.rb +51 -13
- data/lib/dry/monads/unit.rb +6 -2
- data/lib/dry/monads/validated.rb +27 -20
- data/lib/dry/monads/version.rb +3 -1
- data/lib/json/add/dry/monads/maybe.rb +4 -3
- metadata +27 -75
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/ci.yml +0 -74
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -34
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.rubocop.yml +0 -89
- data/.yardopts +0 -4
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -23
- data/Rakefile +0 -6
- data/bin/console +0 -16
- data/bin/setup +0 -7
- data/docsite/source/case-equality.html.md +0 -42
- data/docsite/source/do-notation.html.md +0 -207
- data/docsite/source/getting-started.html.md +0 -142
- data/docsite/source/index.html.md +0 -179
- data/docsite/source/list.html.md +0 -87
- data/docsite/source/maybe.html.md +0 -146
- data/docsite/source/pattern-matching.html.md +0 -68
- data/docsite/source/result.html.md +0 -190
- data/docsite/source/task.html.md +0 -126
- data/docsite/source/tracing-failures.html.md +0 -32
- data/docsite/source/try.html.md +0 -76
- data/docsite/source/unit.html.md +0 -36
- data/docsite/source/validated.html.md +0 -88
- data/log/.gitkeep +0 -0
data/lib/dry/monads/result.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
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
|
-
|
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)
|
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
|
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
|
-
|
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(
|
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
|
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
|
-
#
|
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
|
486
|
+
require "dry/monads/registry"
|
460
487
|
register_mixin(:result, Result::Mixin)
|
461
488
|
end
|
462
489
|
end
|
@@ -1,35 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
37
|
+
private
|
28
38
|
|
29
|
-
|
30
|
-
|
39
|
+
def included(base)
|
40
|
+
super
|
31
41
|
|
32
|
-
|
42
|
+
base.include(@mod)
|
43
|
+
end
|
33
44
|
end
|
34
45
|
end
|
35
46
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
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 #{
|
133
|
+
raise TypeError, "Cannot apply #{val.inspect} to #{@value.inspect}"
|
122
134
|
end
|
123
135
|
|
124
|
-
Undefined.default(val)
|
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
|
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
|
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.
|
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 >=
|
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,
|
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
|
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.
|
304
|
-
# Dry::Monads.None.
|
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.
|
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
|
data/lib/dry/monads/task.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
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]
|
50
|
-
#
|
51
|
-
#
|
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
|
-
|
136
|
+
"value=()"
|
133
137
|
else
|
134
138
|
"value=#{value!.inspect}"
|
135
139
|
end
|
136
140
|
when :rejected
|
137
|
-
"error=#{
|
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
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
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)
|
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
|
317
|
+
require "dry/monads/registry"
|
315
318
|
register_mixin(:task, Task::Mixin)
|
316
319
|
end
|
317
320
|
end
|