dry-monads 0.0.2 → 0.1.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/.rubocop.yml +20 -0
- data/.travis.yml +6 -6
- data/README.md +2 -1
- data/dry-monads.gemspec +1 -0
- data/lib/dry/monads.rb +14 -0
- data/lib/dry/monads/either.rb +83 -16
- data/lib/dry/monads/maybe.rb +86 -16
- data/lib/dry/monads/try.rb +101 -19
- data/lib/dry/monads/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac64c7b3b46d180bb1109ba4272763f79ffc9f57
|
4
|
+
data.tar.gz: a44861c02c43c629b65cbada6d3cae5fc2f56917
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2f903e40b08f17b314f95c773b0b161c084da2df3e59d56cd570c0f104166711f7ac8091fc1901d8118cfc36590493ab08f2d950cee914d83c1fa99855f5a4d
|
7
|
+
data.tar.gz: ef594ee9cc4766bbe519f12d13c398b4e7c8ae8458ec7a551348e559ce2e76de23fd9fcbaf4b7cbaa85b64feac074956004b83f96ce839320a2b88f4325fedc8
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
inherit_from: .rubocop_todo.yml
|
2
2
|
|
3
|
+
AllCops:
|
4
|
+
Exclude:
|
5
|
+
- 'spec/integration/either_spec.rb'
|
6
|
+
- 'vendor/**/*'
|
7
|
+
|
3
8
|
Style/Documentation:
|
4
9
|
Enabled: false
|
5
10
|
|
@@ -24,6 +29,21 @@ Style/MultilineBlockChain:
|
|
24
29
|
Style/GuardClause:
|
25
30
|
Enabled: false
|
26
31
|
|
32
|
+
Style/SymbolProc:
|
33
|
+
Exclude:
|
34
|
+
# This rule is broken in specs on purpose to make examples clearer
|
35
|
+
- 'spec/**/*'
|
36
|
+
|
37
|
+
Style/SignalException:
|
38
|
+
Exclude:
|
39
|
+
- 'spec/**/*'
|
40
|
+
|
41
|
+
Style/ModuleFunction:
|
42
|
+
Enabled: false
|
43
|
+
|
44
|
+
Style/RescueModifier:
|
45
|
+
Enabled: false
|
46
|
+
|
27
47
|
Metrics/LineLength:
|
28
48
|
Max: 110
|
29
49
|
|
data/.travis.yml
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
3
|
cache: bundler
|
4
|
-
bundler_args: --without benchmarks
|
4
|
+
bundler_args: --without benchmarks
|
5
5
|
script:
|
6
6
|
- bundle exec rake spec
|
7
|
+
- bundle exec rubocop
|
7
8
|
rvm:
|
8
|
-
- 2.
|
9
|
-
- 2.
|
10
|
-
- 2.
|
11
|
-
- 2.3.0
|
9
|
+
- 2.1.10
|
10
|
+
- 2.2.5
|
11
|
+
- 2.3.1
|
12
12
|
- rbx-2
|
13
|
-
- jruby-
|
13
|
+
- jruby-9.1.1.0
|
14
14
|
- ruby-head
|
15
15
|
env:
|
16
16
|
global:
|
data/README.md
CHANGED
@@ -11,6 +11,7 @@
|
|
11
11
|
[][code_climate]
|
12
12
|
[][code_climate]
|
13
13
|
[][inch]
|
14
|
+

|
14
15
|
|
15
16
|
Monads for Ruby.
|
16
17
|
|
@@ -46,4 +47,4 @@ prompt that will allow you to experiment.
|
|
46
47
|
|
47
48
|
## Contributing
|
48
49
|
|
49
|
-
Bug reports and pull requests are welcome on GitHub at
|
50
|
+
Bug reports and pull requests are welcome on GitHub at <https://github.com/dry-rb/dry-monads>.
|
data/dry-monads.gemspec
CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.bindir = 'exe'
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
27
|
spec.require_paths = ['lib']
|
28
|
+
spec.add_dependency 'dry-equalizer'
|
28
29
|
|
29
30
|
spec.add_development_dependency 'bundler'
|
30
31
|
spec.add_development_dependency 'rake'
|
data/lib/dry/monads.rb
CHANGED
@@ -1,27 +1,41 @@
|
|
1
|
+
require 'dry/equalizer'
|
1
2
|
require 'dry/monads/either'
|
2
3
|
require 'dry/monads/maybe'
|
3
4
|
require 'dry/monads/try'
|
4
5
|
|
5
6
|
module Dry
|
7
|
+
# @api public
|
6
8
|
module Monads
|
7
9
|
extend self
|
8
10
|
|
11
|
+
# Stores the given value in one of the subtypes of {Maybe} monad.
|
12
|
+
# It is essentially a wrapper for {Maybe.lift}.
|
13
|
+
#
|
14
|
+
# @param value [Object] the value to be stored in the monad
|
15
|
+
# @return [Maybe::Some, Maybe::None]
|
9
16
|
def Maybe(value)
|
10
17
|
Maybe.lift(value)
|
11
18
|
end
|
12
19
|
|
20
|
+
# @param value [Object] the value to be stored in the monad
|
21
|
+
# @return [Maybe::Some]
|
13
22
|
def Some(value)
|
14
23
|
Maybe::Some.new(value)
|
15
24
|
end
|
16
25
|
|
26
|
+
# @return [Maybe::None]
|
17
27
|
def None
|
18
28
|
Maybe::Some::None.instance
|
19
29
|
end
|
20
30
|
|
31
|
+
# @param value [Object] the value to be stored in the monad
|
32
|
+
# @return [Either::Right]
|
21
33
|
def Right(value)
|
22
34
|
Either::Right.new(value)
|
23
35
|
end
|
24
36
|
|
37
|
+
# @param value [Object] the value to be stored in the monad
|
38
|
+
# @return [Either::Left]
|
25
39
|
def Left(value)
|
26
40
|
Either::Left.new(value)
|
27
41
|
end
|
data/lib/dry/monads/either.rb
CHANGED
@@ -1,100 +1,167 @@
|
|
1
1
|
module Dry
|
2
2
|
module Monads
|
3
|
+
# Represents a value which is either correct or an error.
|
4
|
+
#
|
5
|
+
# @api public
|
3
6
|
class Either
|
7
|
+
include Dry::Equalizer(:right, :left)
|
4
8
|
attr_reader :right, :left
|
5
9
|
|
6
|
-
|
7
|
-
other.is_a?(Either) && right == other.right && left == other.left
|
8
|
-
end
|
9
|
-
|
10
|
+
# Returns true for an instance of a {Either::Right} monad.
|
10
11
|
def right?
|
11
12
|
is_a? Right
|
12
13
|
end
|
13
14
|
alias success? right?
|
14
15
|
|
16
|
+
# Returns true for an instance of a {Either::Left} monad.
|
15
17
|
def left?
|
16
18
|
is_a? Left
|
17
19
|
end
|
18
20
|
alias failure? left?
|
19
21
|
|
22
|
+
# Returns self, added to keep the interface compatible with other monads.
|
23
|
+
#
|
24
|
+
# @return [Either::Right, Either::Left]
|
20
25
|
def to_either
|
21
26
|
self
|
22
27
|
end
|
23
28
|
|
29
|
+
# Represents a value that is in a correct state, i.e. everything went right.
|
30
|
+
#
|
31
|
+
# @api public
|
24
32
|
class Right < Either
|
25
33
|
alias value right
|
26
34
|
|
35
|
+
# @param right [Object] a value in a correct state
|
27
36
|
def initialize(right)
|
28
37
|
@right = right
|
29
38
|
end
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
40
|
+
# Calls the passed in Proc object with value stored in self
|
41
|
+
# and returns the result.
|
42
|
+
#
|
43
|
+
# If proc is nil, it expects a block to be given and will yield to it.
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
# Dry::Monads.Right(4).bind(&:succ) # => 5
|
47
|
+
#
|
48
|
+
# @param [Array<Object>] args arguments that will be passed to a block
|
49
|
+
# if one was given, otherwise the first
|
50
|
+
# value assumed to be a Proc (callable)
|
51
|
+
# object and the rest of args will be passed
|
52
|
+
# to this object along with the internal value
|
53
|
+
# @return [Object] result of calling proc or block on the internal value
|
54
|
+
def bind(*args)
|
55
|
+
if block_given?
|
56
|
+
yield(value, *args)
|
34
57
|
else
|
35
|
-
|
58
|
+
args[0].call(value, *args.drop(1))
|
36
59
|
end
|
37
60
|
end
|
38
61
|
|
39
|
-
|
40
|
-
|
62
|
+
# Does the same thing as #bind except it also wraps the value
|
63
|
+
# in an instance of Either::Right monad. This allows for easier
|
64
|
+
# chaining of calls.
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
# Dry::Monads.Right(4).fmap(&:succ).fmap(->(n) { n**2 }) # => Right(25)
|
68
|
+
#
|
69
|
+
# @param [Array<Object>] args arguments will be transparently passed through to #bind
|
70
|
+
# @return [Either::Right]
|
71
|
+
def fmap(*args, &block)
|
72
|
+
Right.new(bind(*args, &block))
|
41
73
|
end
|
42
74
|
|
43
|
-
|
75
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
76
|
+
# identical to that of {Either::Left}.
|
77
|
+
#
|
78
|
+
# @return [Either::Right]
|
79
|
+
def or(*)
|
44
80
|
self
|
45
81
|
end
|
46
82
|
|
83
|
+
# @return [String]
|
47
84
|
def to_s
|
48
85
|
"Right(#{value.inspect})"
|
49
86
|
end
|
50
87
|
alias inspect to_s
|
51
88
|
|
89
|
+
# @return [Maybe::Some]
|
52
90
|
def to_maybe
|
53
91
|
Maybe::Some.new(value)
|
54
92
|
end
|
55
93
|
end
|
56
94
|
|
95
|
+
# Represents a value that is in an incorrect state, i.e. something went wrong.
|
96
|
+
#
|
97
|
+
# @api public
|
57
98
|
class Left < Either
|
58
99
|
alias value left
|
59
100
|
|
101
|
+
# @param left [Object] a value in an error state
|
60
102
|
def initialize(left)
|
61
103
|
@left = left
|
62
104
|
end
|
63
105
|
|
64
|
-
|
106
|
+
# Ignores the input parameter and returns self. It exists to keep the interface
|
107
|
+
# identical to that of {Either::Right}.
|
108
|
+
#
|
109
|
+
# @return [Either::Left]
|
110
|
+
def bind(*)
|
65
111
|
self
|
66
112
|
end
|
67
113
|
|
68
|
-
|
114
|
+
# Ignores the input parameter and returns self. It exists to keep the interface
|
115
|
+
# identical to that of {Either::Right}.
|
116
|
+
#
|
117
|
+
# @return [Either::Left]
|
118
|
+
def fmap(*)
|
69
119
|
self
|
70
120
|
end
|
71
121
|
|
72
|
-
|
122
|
+
# If a block is given passes internal value to it and returns the result,
|
123
|
+
# otherwise simply returns the parameter val.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# Dry::Monads.Left(ArgumentError.new('error message')).or(&:message) # => "error message"
|
127
|
+
#
|
128
|
+
# @param [Array<Object>] args arguments that will be passed to a block
|
129
|
+
# if one was given, otherwise the first
|
130
|
+
# value will be returned
|
131
|
+
# @return [Object]
|
132
|
+
def or(*args)
|
73
133
|
if block_given?
|
74
|
-
yield(value)
|
134
|
+
yield(value, *args)
|
75
135
|
else
|
76
|
-
|
136
|
+
args[0]
|
77
137
|
end
|
78
138
|
end
|
79
139
|
|
140
|
+
# @return [String]
|
80
141
|
def to_s
|
81
142
|
"Left(#{value.inspect})"
|
82
143
|
end
|
83
144
|
alias inspect to_s
|
84
145
|
|
146
|
+
# @return [Maybe::None]
|
85
147
|
def to_maybe
|
86
148
|
Maybe::None.instance
|
87
149
|
end
|
88
150
|
end
|
89
151
|
|
152
|
+
# A module that can be included for easier access to Either monads.
|
90
153
|
module Mixin
|
91
154
|
Right = Right
|
92
155
|
Left = Left
|
93
156
|
|
157
|
+
# @param value [Object] the value to be stored in the monad
|
158
|
+
# @return [Either::Right]
|
94
159
|
def Right(value)
|
95
160
|
Right.new(value)
|
96
161
|
end
|
97
162
|
|
163
|
+
# @param value [Object] the value to be stored in the monad
|
164
|
+
# @return [Either::Left]
|
98
165
|
def Left(value)
|
99
166
|
Left.new(value)
|
100
167
|
end
|
data/lib/dry/monads/maybe.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
1
|
module Dry
|
2
2
|
module Monads
|
3
|
+
# Represents a value which can exist or not, i.e. it could be nil.
|
4
|
+
#
|
5
|
+
# @api public
|
3
6
|
class Maybe
|
7
|
+
include Dry::Equalizer(:value)
|
8
|
+
|
9
|
+
# Lifts the given value into Maybe::None or Maybe::Some monad.
|
10
|
+
#
|
11
|
+
# @param value [Object] the value to be stored in the monad
|
12
|
+
# @return [Maybe::Some, Maybe::None]
|
4
13
|
def self.lift(value)
|
5
14
|
if value.nil?
|
6
15
|
None.instance
|
@@ -9,22 +18,26 @@ module Dry
|
|
9
18
|
end
|
10
19
|
end
|
11
20
|
|
12
|
-
|
13
|
-
other.is_a?(Maybe) && value == other.value
|
14
|
-
end
|
15
|
-
|
21
|
+
# Returns true for an instance of a {Maybe::None} monad.
|
16
22
|
def none?
|
17
23
|
is_a?(None)
|
18
24
|
end
|
19
25
|
|
26
|
+
# Returns true for an instance of a {Maybe::Some} monad.
|
20
27
|
def some?
|
21
28
|
is_a?(Some)
|
22
29
|
end
|
23
30
|
|
31
|
+
# Returns self, added to keep the interface compatible with that of Either monad types.
|
32
|
+
#
|
33
|
+
# @return [Maybe::Some, Maybe::None]
|
24
34
|
def to_maybe
|
25
35
|
self
|
26
36
|
end
|
27
37
|
|
38
|
+
# Represents a value that is present, i.e. not nil.
|
39
|
+
#
|
40
|
+
# @api public
|
28
41
|
class Some < Maybe
|
29
42
|
attr_reader :value
|
30
43
|
|
@@ -33,71 +46,128 @@ module Dry
|
|
33
46
|
@value = value
|
34
47
|
end
|
35
48
|
|
36
|
-
|
37
|
-
|
38
|
-
|
49
|
+
# Calls the passed in Proc object with value stored in self
|
50
|
+
# and returns the result.
|
51
|
+
#
|
52
|
+
# If proc is nil, it expects a block to be given and will yield to it.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# Dry::Monads.Some(4).bind(&:succ) # => 5
|
56
|
+
#
|
57
|
+
# @param [Array<Object>] args arguments that will be passed to a block
|
58
|
+
# if one was given, otherwise the first
|
59
|
+
# value assumed to be a Proc (callable)
|
60
|
+
# object and the rest of args will be passed
|
61
|
+
# to this object along with the internal value
|
62
|
+
# @return [Object] result of calling proc or block on the internal value
|
63
|
+
def bind(*args)
|
64
|
+
if block_given?
|
65
|
+
yield(value, *args)
|
39
66
|
else
|
40
|
-
|
67
|
+
args[0].call(value, *args.drop(1))
|
41
68
|
end
|
42
69
|
end
|
43
70
|
|
44
|
-
|
45
|
-
|
71
|
+
# Does the same thing as #bind except it also wraps the value
|
72
|
+
# in an instance of Maybe::Some monad. This allows for easier
|
73
|
+
# chaining of calls.
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# Dry::Monads.Some(4).fmap(&:succ).fmap(->(n) { n**2 }) # => Some(25)
|
77
|
+
#
|
78
|
+
# @param [Array<Object>] args arguments will be transparently passed through to #bind
|
79
|
+
# @return [Maybe::Some, Maybe::None] Lifted result, i.e. nil will be mapped to None,
|
80
|
+
# other values will be wrapped with Some
|
81
|
+
def fmap(*args, &block)
|
82
|
+
self.class.lift(bind(*args, &block))
|
46
83
|
end
|
47
84
|
|
48
|
-
|
85
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
86
|
+
# identical to that of {Maybe::None}.
|
87
|
+
#
|
88
|
+
# @return [Maybe::Some]
|
89
|
+
def or(*)
|
49
90
|
self
|
50
91
|
end
|
51
92
|
|
93
|
+
# @return [String]
|
52
94
|
def to_s
|
53
95
|
"Some(#{value.inspect})"
|
54
96
|
end
|
55
97
|
alias inspect to_s
|
56
98
|
end
|
57
99
|
|
100
|
+
# Represents an absence of a value, i.e. the value nil.
|
101
|
+
#
|
102
|
+
# @api public
|
58
103
|
class None < Maybe
|
59
104
|
@instance = new
|
60
105
|
singleton_class.send(:attr_reader, :instance)
|
61
106
|
|
107
|
+
# @return [nil]
|
62
108
|
def value
|
63
109
|
nil
|
64
110
|
end
|
65
111
|
|
66
|
-
|
112
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
113
|
+
# identical to that of {Maybe::Some}.
|
114
|
+
#
|
115
|
+
# @return [Maybe::None]
|
116
|
+
def bind(*)
|
67
117
|
self
|
68
118
|
end
|
69
119
|
|
70
|
-
|
120
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
121
|
+
# identical to that of {Maybe::Some}.
|
122
|
+
#
|
123
|
+
# @return [Maybe::None]
|
124
|
+
def fmap(*)
|
71
125
|
self
|
72
126
|
end
|
73
127
|
|
74
|
-
|
128
|
+
# If a block is given passes internal value to it and returns the result,
|
129
|
+
# otherwise simply returns the parameter val.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# Dry::Monads.None.or('no value') # => "no value"
|
133
|
+
# Dry::Monads.None.or { Time.now } # => current time
|
134
|
+
#
|
135
|
+
# @param val [Object, nil]
|
136
|
+
# @return [Object]
|
137
|
+
def or(*args)
|
75
138
|
if block_given?
|
76
|
-
yield(
|
139
|
+
yield(*args)
|
77
140
|
else
|
78
|
-
|
141
|
+
args[0]
|
79
142
|
end
|
80
143
|
end
|
81
144
|
|
145
|
+
# @return [String]
|
82
146
|
def to_s
|
83
147
|
'None'
|
84
148
|
end
|
85
149
|
alias inspect to_s
|
86
150
|
end
|
87
151
|
|
152
|
+
# A module that can be included for easier access to Maybe monads.
|
88
153
|
module Mixin
|
89
154
|
Maybe = Maybe
|
90
155
|
Some = Some
|
91
156
|
None = None
|
92
157
|
|
158
|
+
# @param value [Object] the value to be stored in the monad
|
159
|
+
# @return [Maybe::Some, Maybe::None]
|
93
160
|
def Maybe(value)
|
94
161
|
Maybe.lift(value)
|
95
162
|
end
|
96
163
|
|
164
|
+
# @param value [Object] the value to be stored in the monad
|
165
|
+
# @return [Maybe::Some]
|
97
166
|
def Some(value)
|
98
167
|
Some.new(value)
|
99
168
|
end
|
100
169
|
|
170
|
+
# @return [Maybe::None]
|
101
171
|
def None
|
102
172
|
None.instance
|
103
173
|
end
|
data/lib/dry/monads/try.rb
CHANGED
@@ -1,96 +1,178 @@
|
|
1
1
|
module Dry
|
2
2
|
module Monads
|
3
|
+
# Represents a value which can be either success or a failure (an exception).
|
4
|
+
# Use it to wrap code that can raise exceptions.
|
5
|
+
#
|
6
|
+
# @api public
|
3
7
|
class Try
|
4
8
|
attr_reader :exception, :value
|
5
9
|
|
10
|
+
# Calls the passed in proc object and if successful stores the result in a
|
11
|
+
# {Try::Success} monad, but if one of the specified exceptions was raised it stores
|
12
|
+
# it in a {Try::Failure} monad.
|
13
|
+
#
|
14
|
+
# @param exceptions [Array<Exception>] list of exceptions to be rescued
|
15
|
+
# @param f [Proc] the proc to be called
|
16
|
+
# @return [Try::Success, Try::Failure]
|
6
17
|
def self.lift(exceptions, f)
|
7
18
|
Success.new(exceptions, f.call)
|
8
19
|
rescue *exceptions => e
|
9
20
|
Failure.new(e)
|
10
21
|
end
|
11
22
|
|
23
|
+
# Returns true for an instance of a {Try::Success} monad.
|
12
24
|
def success?
|
13
25
|
is_a? Success
|
14
26
|
end
|
15
27
|
|
28
|
+
# Returns true for an instance of a {Try::Failure} monad.
|
16
29
|
def failure?
|
17
30
|
is_a? Failure
|
18
31
|
end
|
19
32
|
|
33
|
+
# Represents a result of a successful execution.
|
34
|
+
#
|
35
|
+
# @api public
|
20
36
|
class Success < Try
|
37
|
+
include Dry::Equalizer(:value, :catchable)
|
21
38
|
attr_reader :catchable
|
22
39
|
|
40
|
+
# @param exceptions [Array<Exception>] list of exceptions to be rescued
|
41
|
+
# @param value [Object] the value to be stored in the monad
|
23
42
|
def initialize(exceptions, value)
|
24
43
|
@catchable = exceptions
|
25
44
|
@value = value
|
26
45
|
end
|
27
46
|
|
28
|
-
|
29
|
-
|
30
|
-
|
47
|
+
# Calls the passed in Proc object with value stored in self
|
48
|
+
# and returns the result.
|
49
|
+
#
|
50
|
+
# If proc is nil, it expects a block to be given and will yield to it.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# success = Dry::Monads::Try::Success.new(ZeroDivisionError, 10)
|
54
|
+
# success.bind(->(n) { n / 2 }) # => 5
|
55
|
+
# success.bind { |n| n / 0 } # => Try::Failure(ZeroDivisionError: divided by 0)
|
56
|
+
#
|
57
|
+
# @param [Array<Object>] args arguments that will be passed to a block
|
58
|
+
# if one was given, otherwise the first
|
59
|
+
# value assumed to be a Proc (callable)
|
60
|
+
# object and the rest of args will be passed
|
61
|
+
# to this object along with the internal value
|
62
|
+
# @return [Object, Try::Failure]
|
63
|
+
def bind(*args)
|
64
|
+
if block_given?
|
65
|
+
yield(value, *args)
|
31
66
|
else
|
32
|
-
|
67
|
+
args[0].call(value, *args.drop(1))
|
33
68
|
end
|
34
69
|
rescue *catchable => e
|
35
70
|
Failure.new(e)
|
36
71
|
end
|
37
72
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
73
|
+
# Does the same thing as #bind except it also wraps the value
|
74
|
+
# in an instance of a Try monad. This allows for easier
|
75
|
+
# chaining of calls.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# success = Dry::Monads::Try::Success.new(ZeroDivisionError, 10)
|
79
|
+
# success.fmap(&:succ).fmap(&:succ).value # => 12
|
80
|
+
# success.fmap(&:succ).fmap { |n| n / 0 }.fmap(&:succ).value # => nil
|
81
|
+
#
|
82
|
+
# @param [Array<Object>] args extra arguments for the block, arguments are being processes
|
83
|
+
# just as in #bind
|
84
|
+
# @return [Try::Success, Try::Failure]
|
85
|
+
def fmap(*args, &block)
|
86
|
+
if block
|
87
|
+
Try.lift(catchable, -> { yield(value, *args) })
|
88
|
+
else
|
89
|
+
Try.lift(catchable, -> { args[0].call(value, *args.drop(1)) })
|
90
|
+
end
|
44
91
|
end
|
45
92
|
|
93
|
+
# @return [Maybe]
|
46
94
|
def to_maybe
|
47
|
-
Dry::Monads::Maybe(
|
95
|
+
Dry::Monads::Maybe(value)
|
48
96
|
end
|
49
97
|
|
98
|
+
# @return [Either::Right]
|
50
99
|
def to_either
|
51
|
-
Dry::Monads::Right(
|
100
|
+
Dry::Monads::Right(value)
|
52
101
|
end
|
53
102
|
|
103
|
+
# @return [String]
|
54
104
|
def to_s
|
55
105
|
"Try::Success(#{value.inspect})"
|
56
106
|
end
|
57
107
|
alias inspect to_s
|
58
108
|
end
|
59
109
|
|
110
|
+
# Represents a result of a failed execution.
|
111
|
+
#
|
112
|
+
# @api public
|
60
113
|
class Failure < Try
|
114
|
+
include Dry::Equalizer(:exception)
|
115
|
+
|
116
|
+
# @param exception [Exception]
|
61
117
|
def initialize(exception)
|
62
118
|
@exception = exception
|
63
119
|
end
|
64
120
|
|
65
|
-
|
121
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
122
|
+
# identical to that of {Try::Success}.
|
123
|
+
#
|
124
|
+
# @return [Try::Failure]
|
125
|
+
def bind(*)
|
66
126
|
self
|
67
127
|
end
|
68
128
|
|
69
|
-
|
129
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
130
|
+
# identical to that of {Try::Success}.
|
131
|
+
#
|
132
|
+
# @return [Try::Failure]
|
133
|
+
def fmap(*)
|
70
134
|
self
|
71
135
|
end
|
72
136
|
|
137
|
+
# @return [Maybe::None]
|
73
138
|
def to_maybe
|
74
139
|
Dry::Monads::None()
|
75
140
|
end
|
76
141
|
|
142
|
+
# @return [Either::Left]
|
77
143
|
def to_either
|
78
|
-
Dry::Monads::Left(
|
79
|
-
end
|
80
|
-
|
81
|
-
def ==(other)
|
82
|
-
other.is_a?(Failure) && @exception == other.exception
|
144
|
+
Dry::Monads::Left(exception)
|
83
145
|
end
|
84
146
|
|
147
|
+
# @return [String]
|
85
148
|
def to_s
|
86
149
|
"Try::Failure(#{exception.class}: #{exception.message})"
|
87
150
|
end
|
88
151
|
alias inspect to_s
|
89
152
|
end
|
90
153
|
|
154
|
+
# A module that can be included for easier access to Try monads.
|
155
|
+
#
|
156
|
+
# @example
|
157
|
+
# class Foo
|
158
|
+
# include Dry::Monads::Try::Mixin
|
159
|
+
#
|
160
|
+
# attr_reader :average
|
161
|
+
#
|
162
|
+
# def initialize(total, count)
|
163
|
+
# @average = Try(ZeroDivisionError) { total / count }.value
|
164
|
+
# end
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
# Foo.new(10, 2).average # => 5
|
168
|
+
# Foo.new(10, 0).average # => nil
|
91
169
|
module Mixin
|
92
170
|
Try = Try
|
93
171
|
|
172
|
+
# A convenience wrapper for {Try.lift}.
|
173
|
+
# If no exceptions are provided it falls back to StandardError.
|
174
|
+
# In general, relying on this behaviour is not recommended as it can lead to unnoticed
|
175
|
+
# bugs and it is always better to explicitly specify a list of exceptions if possible.
|
94
176
|
def Try(*exceptions, &f)
|
95
177
|
catchable = exceptions.any? ? exceptions.flatten : [StandardError]
|
96
178
|
Try.lift(catchable, f)
|
data/lib/dry/monads/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-monads
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nikita Shilnikov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-equalizer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|