dry-monads 1.3.5 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +140 -80
- data/LICENSE +1 -1
- data/README.md +5 -4
- data/dry-monads.gemspec +30 -30
- data/lib/dry-monads.rb +1 -1
- data/lib/dry/monads.rb +2 -2
- data/lib/dry/monads/all.rb +2 -2
- data/lib/dry/monads/constants.rb +1 -1
- data/lib/dry/monads/do.rb +52 -18
- data/lib/dry/monads/do/all.rb +36 -17
- data/lib/dry/monads/either.rb +7 -7
- data/lib/dry/monads/errors.rb +5 -2
- data/lib/dry/monads/lazy.rb +15 -4
- data/lib/dry/monads/list.rb +28 -28
- data/lib/dry/monads/maybe.rb +87 -19
- data/lib/dry/monads/registry.rb +10 -10
- data/lib/dry/monads/result.rb +38 -12
- data/lib/dry/monads/result/fixed.rb +33 -24
- data/lib/dry/monads/right_biased.rb +35 -16
- data/lib/dry/monads/task.rb +20 -20
- data/lib/dry/monads/transformer.rb +2 -1
- data/lib/dry/monads/traverse.rb +7 -1
- data/lib/dry/monads/try.rb +45 -12
- data/lib/dry/monads/unit.rb +6 -2
- data/lib/dry/monads/validated.rb +20 -16
- data/lib/dry/monads/version.rb +1 -1
- data/lib/json/add/dry/monads/maybe.rb +3 -3
- metadata +18 -69
- 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 -30
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/ci.yml +0 -52
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -56
- data/.gitignore +0 -10
- data/.rspec +0 -4
- data/.rubocop.yml +0 -101
- data/.yardopts +0 -4
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -19
- data/Gemfile.devtools +0 -14
- data/Rakefile +0 -8
- data/bin/.gitkeep +0 -0
- data/bin/console +0 -17
- 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/project.yml +0 -2
data/lib/dry/monads/list.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
5
|
+
require "dry/monads/maybe"
|
6
|
+
require "dry/monads/task"
|
7
|
+
require "dry/monads/result"
|
8
|
+
require "dry/monads/try"
|
9
|
+
require "dry/monads/validated"
|
10
|
+
require "dry/monads/transformer"
|
11
|
+
require "dry/monads/curry"
|
12
12
|
|
13
13
|
module Dry
|
14
14
|
module Monads
|
@@ -83,7 +83,7 @@ module Dry
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
extend Dry::Core::Deprecations[:
|
86
|
+
extend Dry::Core::Deprecations[:"dry-monads"]
|
87
87
|
|
88
88
|
include Dry::Equalizer(:value, :type)
|
89
89
|
include Transformer
|
@@ -136,14 +136,14 @@ module Dry
|
|
136
136
|
end
|
137
137
|
|
138
138
|
# Maps a block over the list. Acts as `Array#map`.
|
139
|
-
#
|
139
|
+
# If called without a block, this method returns an enumerator, not a List
|
140
140
|
#
|
141
141
|
# @return [List,Enumerator]
|
142
142
|
def map(&block)
|
143
|
-
if
|
143
|
+
if block_given?
|
144
144
|
fmap(block)
|
145
145
|
else
|
146
|
-
value.map
|
146
|
+
value.map
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
@@ -165,7 +165,7 @@ module Dry
|
|
165
165
|
#
|
166
166
|
# @return [String]
|
167
167
|
def inspect
|
168
|
-
type_ann = typed? ? "<#{type.name.split(
|
168
|
+
type_ann = typed? ? "<#{type.name.split("::").last}>" : ""
|
169
169
|
"List#{type_ann}#{value.inspect}"
|
170
170
|
end
|
171
171
|
alias_method :to_s, :inspect
|
@@ -192,8 +192,8 @@ module Dry
|
|
192
192
|
#
|
193
193
|
# @param initial [Object] Initial value
|
194
194
|
# @return [Object]
|
195
|
-
def fold_left(initial)
|
196
|
-
value.reduce(initial
|
195
|
+
def fold_left(initial, &block)
|
196
|
+
value.reduce(initial, &block)
|
197
197
|
end
|
198
198
|
alias_method :foldl, :fold_left
|
199
199
|
alias_method :reduce, :fold_left
|
@@ -224,8 +224,8 @@ module Dry
|
|
224
224
|
# Filters elements with a block
|
225
225
|
#
|
226
226
|
# @return [List]
|
227
|
-
def filter
|
228
|
-
coerce(value.select
|
227
|
+
def filter(&block)
|
228
|
+
coerce(value.select(&block))
|
229
229
|
end
|
230
230
|
alias_method :select, :filter
|
231
231
|
|
@@ -265,10 +265,10 @@ module Dry
|
|
265
265
|
def typed(type = nil)
|
266
266
|
if type.nil?
|
267
267
|
if size.zero?
|
268
|
-
raise ArgumentError,
|
268
|
+
raise ArgumentError, "Cannot infer a monad for an empty list"
|
269
269
|
else
|
270
270
|
self.class.warn(
|
271
|
-
|
271
|
+
"Automatic monad inference is deprecated, pass a type explicitly "\
|
272
272
|
"or use a predefined constant, e.g. List::Result\n"\
|
273
273
|
"#{caller.find { |l| l !~ %r{(lib/dry/monads)|(gems)} }}"
|
274
274
|
)
|
@@ -298,7 +298,7 @@ module Dry
|
|
298
298
|
# @return [Monad] Result is a monadic value
|
299
299
|
def traverse(proc = nil, &block)
|
300
300
|
unless typed?
|
301
|
-
raise StandardError,
|
301
|
+
raise StandardError, "Cannot traverse an untyped list"
|
302
302
|
end
|
303
303
|
|
304
304
|
cons = type.pure { |list, i| list + List.pure(i) }
|
@@ -315,8 +315,8 @@ module Dry
|
|
315
315
|
#
|
316
316
|
# @param list [List]
|
317
317
|
# @return [List]
|
318
|
-
def apply(list = Undefined)
|
319
|
-
v = Undefined.default(list)
|
318
|
+
def apply(list = Undefined, &block)
|
319
|
+
v = Undefined.default(list, &block)
|
320
320
|
fmap(Curry).bind { |f| v.fmap { |x| f.(x) } }
|
321
321
|
end
|
322
322
|
|
@@ -329,7 +329,7 @@ module Dry
|
|
329
329
|
|
330
330
|
# Returns self.
|
331
331
|
#
|
332
|
-
# @return [
|
332
|
+
# @return [List]
|
333
333
|
def to_monad
|
334
334
|
self
|
335
335
|
end
|
@@ -345,7 +345,7 @@ module Dry
|
|
345
345
|
# Some(n / divisor)
|
346
346
|
# end
|
347
347
|
# end
|
348
|
-
# # => List[
|
348
|
+
# # => List[2, 4]
|
349
349
|
#
|
350
350
|
# @example without block
|
351
351
|
# List[Some(5), None(), Some(3)].collect.map { |x| x * 2 }
|
@@ -422,10 +422,10 @@ module Dry
|
|
422
422
|
# List of results
|
423
423
|
Result = ListBuilder[Result]
|
424
424
|
|
425
|
-
# List of
|
425
|
+
# List of maybes
|
426
426
|
Maybe = ListBuilder[Maybe]
|
427
427
|
|
428
|
-
# List of
|
428
|
+
# List of tries
|
429
429
|
Try = ListBuilder[Try]
|
430
430
|
|
431
431
|
# List of validation results
|
@@ -449,9 +449,9 @@ module Dry
|
|
449
449
|
end
|
450
450
|
end
|
451
451
|
|
452
|
-
require
|
452
|
+
require "dry/monads/registry"
|
453
453
|
register_mixin(:list, List::Mixin)
|
454
454
|
end
|
455
455
|
end
|
456
456
|
|
457
|
-
require
|
457
|
+
require "dry/monads/traverse"
|
data/lib/dry/monads/maybe.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/core/deprecations"
|
5
|
+
require "dry/core/class_attributes"
|
5
6
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
7
|
+
require "dry/monads/right_biased"
|
8
|
+
require "dry/monads/transformer"
|
9
|
+
require "dry/monads/unit"
|
10
|
+
require "dry/monads/constants"
|
10
11
|
|
11
12
|
module Dry
|
12
13
|
module Monads
|
@@ -15,9 +16,13 @@ module Dry
|
|
15
16
|
# @api public
|
16
17
|
class Maybe
|
17
18
|
include Transformer
|
19
|
+
extend Core::ClassAttributes
|
20
|
+
|
21
|
+
defines :warn_on_implicit_nil_coercion
|
22
|
+
warn_on_implicit_nil_coercion true
|
18
23
|
|
19
24
|
class << self
|
20
|
-
extend Core::Deprecations[:
|
25
|
+
extend Core::Deprecations[:"dry-monads"]
|
21
26
|
|
22
27
|
# Wraps the given value with into a Maybe object.
|
23
28
|
#
|
@@ -105,7 +110,9 @@ module Dry
|
|
105
110
|
end
|
106
111
|
|
107
112
|
def initialize(value = Undefined)
|
108
|
-
raise ArgumentError,
|
113
|
+
raise ArgumentError, "nil cannot be some" if value.nil?
|
114
|
+
|
115
|
+
super()
|
109
116
|
|
110
117
|
@value = Undefined.default(value, Unit)
|
111
118
|
end
|
@@ -121,14 +128,65 @@ module Dry
|
|
121
128
|
# @return [Maybe::Some, Maybe::None] Wrapped result, i.e. nil will be mapped to None,
|
122
129
|
# other values will be wrapped with Some
|
123
130
|
def fmap(*args, &block)
|
131
|
+
next_value = bind(*args, &block)
|
132
|
+
|
133
|
+
if next_value.nil?
|
134
|
+
if self.class.warn_on_implicit_nil_coercion
|
135
|
+
Core::Deprecations.warn(
|
136
|
+
"Block passed to Some#fmap returned `nil` and was chained to None. "\
|
137
|
+
"This is literally an unlawful behavior and it will not be supported in "\
|
138
|
+
"dry-monads 2. \nPlease, replace `.fmap` with `.maybe` in places where you "\
|
139
|
+
"expect `nil` as block result.\n"\
|
140
|
+
"You can opt out of these warnings with\n"\
|
141
|
+
"Dry::Monads::Maybe.warn_on_implicit_nil_coercion false",
|
142
|
+
uplevel: 0,
|
143
|
+
tag: :'dry-monads'
|
144
|
+
)
|
145
|
+
end
|
146
|
+
Monads.None()
|
147
|
+
else
|
148
|
+
Some.new(next_value)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Does the same thing as #bind except it also wraps the value
|
153
|
+
# in an instance of the Maybe monad. This allows for easier
|
154
|
+
# chaining of calls.
|
155
|
+
#
|
156
|
+
# @example
|
157
|
+
# Dry::Monads.Some(4).maybe(&:succ).maybe(->(n) { n**2 }) # => Some(25)
|
158
|
+
# Dry::Monads.Some(4).maybe(&:succ).maybe(->(_) { nil }) # => None()
|
159
|
+
#
|
160
|
+
# @param args [Array<Object>] arguments will be transparently passed through to #bind
|
161
|
+
# @return [Maybe::Some, Maybe::None] Wrapped result, i.e. nil will be mapped to None,
|
162
|
+
# other values will be wrapped with Some
|
163
|
+
def maybe(*args, &block)
|
124
164
|
Maybe.coerce(bind(*args, &block))
|
125
165
|
end
|
126
|
-
|
166
|
+
|
167
|
+
# Accepts a block and runs it against the wrapped value.
|
168
|
+
# If the block returns a trurhy value the result is self,
|
169
|
+
# otherwise None. If no block is given, the value serves
|
170
|
+
# and its result.
|
171
|
+
#
|
172
|
+
# @param with [#call] positional block
|
173
|
+
# @param block [Proc] block
|
174
|
+
#
|
175
|
+
# @return [Maybe::None, Maybe::Some]
|
176
|
+
def filter(with = Undefined, &block)
|
177
|
+
block = Undefined.default(with, block || IDENTITY)
|
178
|
+
|
179
|
+
if block.(@value)
|
180
|
+
self
|
181
|
+
else
|
182
|
+
Monads.None()
|
183
|
+
end
|
184
|
+
end
|
127
185
|
|
128
186
|
# @return [String]
|
129
187
|
def to_s
|
130
188
|
if Unit.equal?(@value)
|
131
|
-
|
189
|
+
"Some()"
|
132
190
|
else
|
133
191
|
"Some(#{@value.inspect})"
|
134
192
|
end
|
@@ -146,7 +204,7 @@ module Dry
|
|
146
204
|
singleton_class.send(:attr_reader, :instance)
|
147
205
|
|
148
206
|
# @api private
|
149
|
-
def self.method_missing(m, *)
|
207
|
+
def self.method_missing(m, *) # rubocop:disable Style/MissingRespondToMissing
|
150
208
|
if (instance.methods(true) - methods(true)).include?(m)
|
151
209
|
raise ConstructorNotAppliedError.new(m, :None)
|
152
210
|
else
|
@@ -161,6 +219,7 @@ module Dry
|
|
161
219
|
attr_reader :trace
|
162
220
|
|
163
221
|
def initialize(trace = RightBiased::Left.trace_caller)
|
222
|
+
super()
|
164
223
|
@trace = trace
|
165
224
|
end
|
166
225
|
|
@@ -202,7 +261,7 @@ module Dry
|
|
202
261
|
|
203
262
|
# @return [String]
|
204
263
|
def to_s
|
205
|
-
|
264
|
+
"None"
|
206
265
|
end
|
207
266
|
alias_method :inspect, :to_s
|
208
267
|
|
@@ -230,6 +289,13 @@ module Dry
|
|
230
289
|
def deconstruct
|
231
290
|
EMPTY_ARRAY
|
232
291
|
end
|
292
|
+
|
293
|
+
# @see Maybe::Some#filter
|
294
|
+
#
|
295
|
+
# @return [Maybe::None]
|
296
|
+
def filter(_ = Undefined)
|
297
|
+
self
|
298
|
+
end
|
233
299
|
end
|
234
300
|
|
235
301
|
# A module that can be included for easier access to Maybe monads.
|
@@ -301,9 +367,9 @@ module Dry
|
|
301
367
|
# None values are removed
|
302
368
|
#
|
303
369
|
# @example
|
304
|
-
# Maybe::Hash.filter(foo: Some(1), bar: Some(2)) # =>
|
305
|
-
# Maybe::Hash.filter(foo: Some(1), bar: None()) # =>
|
306
|
-
# Maybe::Hash.filter(foo: None(), bar: Some(2)) # =>
|
370
|
+
# Maybe::Hash.filter(foo: Some(1), bar: Some(2)) # => { foo: 1, bar: 2 }
|
371
|
+
# Maybe::Hash.filter(foo: Some(1), bar: None()) # => { foo: 1 }
|
372
|
+
# Maybe::Hash.filter(foo: None(), bar: Some(2)) # => { bar: 2 }
|
307
373
|
#
|
308
374
|
# @param hash [::Hash<Object,Maybe>]
|
309
375
|
# @return [::Hash]
|
@@ -325,10 +391,12 @@ module Dry
|
|
325
391
|
|
326
392
|
class Result
|
327
393
|
class Success < Result
|
328
|
-
|
394
|
+
extend Core::Deprecations[:"dry-monads"]
|
395
|
+
|
396
|
+
# @return [Maybe]
|
329
397
|
def to_maybe
|
330
|
-
|
331
|
-
Dry::Monads::Maybe(@value)
|
398
|
+
warn "Success(nil) transformed to None" if @value.nil?
|
399
|
+
::Dry::Monads::Maybe(@value)
|
332
400
|
end
|
333
401
|
end
|
334
402
|
|
@@ -389,7 +457,7 @@ module Dry
|
|
389
457
|
end
|
390
458
|
end
|
391
459
|
|
392
|
-
require
|
460
|
+
require "dry/monads/registry"
|
393
461
|
register_mixin(:maybe, Maybe::Mixin)
|
394
462
|
end
|
395
463
|
end
|
data/lib/dry/monads/registry.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "concurrent/map"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
# Common, idiomatic monads for Ruby
|
@@ -10,16 +10,16 @@ module Dry
|
|
10
10
|
@registry = {}
|
11
11
|
@constructors = nil
|
12
12
|
@paths = {
|
13
|
-
do:
|
14
|
-
lazy:
|
15
|
-
list:
|
16
|
-
maybe:
|
17
|
-
task:
|
18
|
-
try:
|
19
|
-
validated:
|
13
|
+
do: "dry/monads/do/all",
|
14
|
+
lazy: "dry/monads/lazy",
|
15
|
+
list: "dry/monads/list",
|
16
|
+
maybe: "dry/monads/maybe",
|
17
|
+
task: "dry/monads/task",
|
18
|
+
try: "dry/monads/try",
|
19
|
+
validated: "dry/monads/validated",
|
20
20
|
result: [
|
21
|
-
|
22
|
-
|
21
|
+
"dry/monads/result",
|
22
|
+
"dry/monads/result/fixed"
|
23
23
|
]
|
24
24
|
}.freeze
|
25
25
|
@mixins = Concurrent::Map.new
|
data/lib/dry/monads/result.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
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"
|
10
10
|
|
11
11
|
module Dry
|
12
12
|
module Monads
|
@@ -86,6 +86,7 @@ module Dry
|
|
86
86
|
|
87
87
|
# @param value [Object] a value of a successful operation
|
88
88
|
def initialize(value)
|
89
|
+
super()
|
89
90
|
@value = value
|
90
91
|
end
|
91
92
|
|
@@ -134,7 +135,7 @@ module Dry
|
|
134
135
|
# @return [String]
|
135
136
|
def to_s
|
136
137
|
if Unit.equal?(@value)
|
137
|
-
|
138
|
+
"Success()"
|
138
139
|
else
|
139
140
|
"Success(#{@value.inspect})"
|
140
141
|
end
|
@@ -147,6 +148,13 @@ module Dry
|
|
147
148
|
def flip
|
148
149
|
Failure.new(@value, RightBiased::Left.trace_caller)
|
149
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
|
150
158
|
end
|
151
159
|
|
152
160
|
# Represents a value of a failed operation.
|
@@ -188,6 +196,7 @@ module Dry
|
|
188
196
|
# @param value [Object] failure value
|
189
197
|
# @param trace [String] caller line
|
190
198
|
def initialize(value, trace = RightBiased::Left.trace_caller)
|
199
|
+
super()
|
191
200
|
@value = value
|
192
201
|
@trace = trace
|
193
202
|
end
|
@@ -218,7 +227,8 @@ module Dry
|
|
218
227
|
# otherwise simply returns the first argument.
|
219
228
|
#
|
220
229
|
# @example
|
221
|
-
# Dry::Monads.Failure(ArgumentError.new('error message')).or(&:message)
|
230
|
+
# Dry::Monads.Failure(ArgumentError.new('error message')).or(&:message)
|
231
|
+
# # => "error message"
|
222
232
|
#
|
223
233
|
# @param args [Array<Object>] arguments that will be passed to a block
|
224
234
|
# if one was given, otherwise the first
|
@@ -232,7 +242,8 @@ module Dry
|
|
232
242
|
end
|
233
243
|
end
|
234
244
|
|
235
|
-
# 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.
|
236
247
|
#
|
237
248
|
# @example
|
238
249
|
# Dry::Monads.Failure.new('no value').or_fmap('value') # => Success("value")
|
@@ -247,7 +258,7 @@ module Dry
|
|
247
258
|
# @return [String]
|
248
259
|
def to_s
|
249
260
|
if Unit.equal?(@value)
|
250
|
-
|
261
|
+
"Failure()"
|
251
262
|
else
|
252
263
|
"Failure(#{@value.inspect})"
|
253
264
|
end
|
@@ -287,6 +298,21 @@ module Dry
|
|
287
298
|
def either(_, g)
|
288
299
|
g.(failure)
|
289
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
|
290
316
|
end
|
291
317
|
|
292
318
|
# A module that can be included for easier access to Result monads.
|
@@ -448,7 +474,7 @@ module Dry
|
|
448
474
|
end
|
449
475
|
|
450
476
|
class Invalid < Validated
|
451
|
-
#
|
477
|
+
# Converts to Result::Failure
|
452
478
|
#
|
453
479
|
# @return [Result::Failure]
|
454
480
|
def to_result
|
@@ -457,7 +483,7 @@ module Dry
|
|
457
483
|
end
|
458
484
|
end
|
459
485
|
|
460
|
-
require
|
486
|
+
require "dry/monads/registry"
|
461
487
|
register_mixin(:result, Result::Mixin)
|
462
488
|
end
|
463
489
|
end
|