dry-monads 0.2.1 → 0.3.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/.travis.yml +10 -10
- data/CHANGELOG.md +67 -0
- data/Gemfile +1 -1
- data/bin/console +2 -0
- data/lib/dry/monads.rb +1 -0
- data/lib/dry/monads/either.rb +67 -69
- data/lib/dry/monads/list.rb +285 -0
- data/lib/dry/monads/maybe.rb +49 -71
- data/lib/dry/monads/right_biased.rb +148 -0
- data/lib/dry/monads/transformer.rb +42 -0
- data/lib/dry/monads/try.rb +25 -31
- data/lib/dry/monads/version.rb +1 -1
- metadata +7 -5
- data/.rubocop.yml +0 -64
- data/.rubocop_todo.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d763397f947c61f8b46fa0997e72b24b0c6b896
|
4
|
+
data.tar.gz: 99c80e46741e351e19f107dd71c01e8a7a66beaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 134d7bc216b65df0b1ed974fc68d04521ec1766c527848c4df473057ae5175e42447cee84073e69b44d4fe3a8a9399918ffb8c7119d6eb65abf0f7aa873d521c
|
7
|
+
data.tar.gz: b25676d21087677f2a2276b0e09abb3e672ce0d12f4c50aeee88c72afa8aff0d4e6df70ace29efc00b699094b94fe44f14753063d3f63bc2653bc8c50839d5d5
|
data/.travis.yml
CHANGED
@@ -1,25 +1,23 @@
|
|
1
1
|
language: ruby
|
2
2
|
dist: trusty
|
3
|
-
sudo:
|
3
|
+
sudo: false
|
4
4
|
cache: bundler
|
5
5
|
bundler_args: --without benchmarks
|
6
6
|
script:
|
7
7
|
- bundle exec rake spec
|
8
|
-
- bundle exec rubocop
|
9
8
|
after_success:
|
10
|
-
|
9
|
+
# Send coverage report from the job #1 == current MRI release
|
10
|
+
- '[ "${TRAVIS_JOB_NUMBER#*.}" = "1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
|
11
11
|
rvm:
|
12
|
+
- 2.4.0
|
13
|
+
- 2.3.3
|
14
|
+
- 2.2.6
|
12
15
|
- 2.1.10
|
13
|
-
-
|
14
|
-
- 2.3.1
|
15
|
-
- jruby-9.1.5.0
|
16
|
+
- jruby-9.1.7.0
|
16
17
|
- ruby-head
|
17
|
-
- rbx-3
|
18
18
|
env:
|
19
19
|
global:
|
20
20
|
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
21
|
-
before_install:
|
22
|
-
- gem update bundler
|
23
21
|
matrix:
|
24
22
|
allow_failures:
|
25
23
|
- rvm: ruby-head
|
@@ -27,7 +25,9 @@ matrix:
|
|
27
25
|
- rvm: rbx-3
|
28
26
|
include:
|
29
27
|
- rvm: jruby-head
|
30
|
-
before_install: gem
|
28
|
+
before_install: gem update bundler
|
29
|
+
- rvm: rbx-3
|
30
|
+
before_install: gem update bundler
|
31
31
|
|
32
32
|
notifications:
|
33
33
|
email:
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# v0.3.0 2017-03-16
|
2
|
+
|
3
|
+
## Added
|
4
|
+
* Added `Either#either` that accepts two callbacks, runs the first if it is `Right` and the second otherwise (nkondratyev)
|
5
|
+
* Added `#fmap2` and `#fmap3` for mapping over nested structures like `List Either` and `Either Some` (flash-gordon)
|
6
|
+
* Added `Try#value_or` (dsounded)
|
7
|
+
* Added the `List` monad which acts as an immutable `Array` and plays nice with other monads. A common example is a list of `Either`s (flash-gordon)
|
8
|
+
* `#bind` made to work with keyword arguments as extra parameters to the block (flash-gordon)
|
9
|
+
* Added `List#traverse` that "flips" the list with an embedded monad (flash-gordon + damncabbage)
|
10
|
+
* Added `#tee` for all right-biased monads (flash-gordon)
|
11
|
+
|
12
|
+
[Compare v0.3.0...v0.2.1](https://github.com/dry-rb/dry-monads/compare/v0.2.1...v0.3.0)
|
13
|
+
|
14
|
+
# v0.2.1 2016-11-13
|
15
|
+
|
16
|
+
## Added
|
17
|
+
|
18
|
+
* Added `Either#tee` that is similar to `Object#tap` but executes the block only for `Right` instances (saverio-kantox)
|
19
|
+
|
20
|
+
## Fixed
|
21
|
+
|
22
|
+
* `Right(nil).to_maybe` now returns `None` with a warning instead of failing (orisaka)
|
23
|
+
* `Some#value_or` doesn't require an argument because `None#value_or` doesn't require it either if a block was passed (flash-gordon)
|
24
|
+
|
25
|
+
[Compare v0.2.1...v0.2.0](https://github.com/dry-rb/dry-monads/compare/v0.2.0...v0.2.1)
|
26
|
+
|
27
|
+
# v0.2.0 2016-09-18
|
28
|
+
|
29
|
+
## Added
|
30
|
+
|
31
|
+
* Added `Maybe#to_json` as an opt-in extension for serialization to JSON (rocknruby)
|
32
|
+
* Added `Maybe#value_or` which returns you the underlying value with a fallback in a single method call (dsounded)
|
33
|
+
|
34
|
+
[Compare v0.1.1...v0.2.0](https://github.com/dry-rb/dry-monads/compare/v0.1.1...v0.2.0)
|
35
|
+
|
36
|
+
# v0.1.1 2016-08-25
|
37
|
+
|
38
|
+
## Fixed
|
39
|
+
|
40
|
+
* Added explicit requires of `dry-equalizer`. This allows to safely load only specific monads (artofhuman)
|
41
|
+
|
42
|
+
[Compare v0.1.0...v0.1.1](https://github.com/dry-rb/dry-monads/compare/v0.1.0...v0.1.1)
|
43
|
+
|
44
|
+
# v0.1.0 2016-08-23
|
45
|
+
|
46
|
+
## Added
|
47
|
+
|
48
|
+
* Support for passing extra arguments to the block in `.bind` and `.fmap` (flash-gordon)
|
49
|
+
|
50
|
+
## Changed
|
51
|
+
|
52
|
+
* Dropped MRI 2.0 support (flash-gordon)
|
53
|
+
|
54
|
+
[Compare v0.0.2...v0.1.0](https://github.com/dry-rb/dry-monads/compare/v0.0.2...v0.1.0)
|
55
|
+
|
56
|
+
# v0.0.2 2016-06-29
|
57
|
+
|
58
|
+
## Added
|
59
|
+
|
60
|
+
* Added `Either#to_either` so that you can rely on duck-typing when you work with different types of monads (timriley)
|
61
|
+
* Added `Maybe#to_maybe` for consistency with `#to_either` (flash-gordon)
|
62
|
+
|
63
|
+
[Compare v0.0.1...v0.0.2](https://github.com/dry-rb/dry-monads/compare/v0.0.1...v0.0.2)
|
64
|
+
|
65
|
+
# v0.0.1 2016-05-02
|
66
|
+
|
67
|
+
Initial release containing `Either`, `Maybe`, and `Try` monads.
|
data/Gemfile
CHANGED
data/bin/console
CHANGED
data/lib/dry/monads.rb
CHANGED
data/lib/dry/monads/either.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'dry/equalizer'
|
2
2
|
|
3
|
+
require 'dry/monads/right_biased'
|
4
|
+
require 'dry/monads/transformer'
|
5
|
+
|
3
6
|
module Dry
|
4
7
|
module Monads
|
5
8
|
# Represents a value which is either correct or an error.
|
@@ -7,19 +10,19 @@ module Dry
|
|
7
10
|
# @api public
|
8
11
|
class Either
|
9
12
|
include Dry::Equalizer(:right, :left)
|
10
|
-
|
13
|
+
include Transformer
|
11
14
|
|
12
|
-
|
13
|
-
def right?
|
14
|
-
is_a? Right
|
15
|
-
end
|
16
|
-
alias success? right?
|
15
|
+
attr_reader :right, :left
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
class << self
|
18
|
+
# Wraps the given value with Right
|
19
|
+
#
|
20
|
+
# @param value [Object] the value to be stored inside Right
|
21
|
+
# @return [Either::Right]
|
22
|
+
def pure(value)
|
23
|
+
Right.new(value)
|
24
|
+
end
|
21
25
|
end
|
22
|
-
alias failure? left?
|
23
26
|
|
24
27
|
# Returns self, added to keep the interface compatible with other monads.
|
25
28
|
#
|
@@ -28,10 +31,20 @@ module Dry
|
|
28
31
|
self
|
29
32
|
end
|
30
33
|
|
34
|
+
# Returns the Either monad.
|
35
|
+
# This is how we're doing polymorphism in Ruby 😕
|
36
|
+
#
|
37
|
+
# @return [Monad]
|
38
|
+
def monad
|
39
|
+
Either
|
40
|
+
end
|
41
|
+
|
31
42
|
# Represents a value that is in a correct state, i.e. everything went right.
|
32
43
|
#
|
33
44
|
# @api public
|
34
45
|
class Right < Either
|
46
|
+
include RightBiased::Right
|
47
|
+
|
35
48
|
alias value right
|
36
49
|
|
37
50
|
# @param right [Object] a value in a correct state
|
@@ -39,27 +52,24 @@ module Dry
|
|
39
52
|
@right = right
|
40
53
|
end
|
41
54
|
|
42
|
-
#
|
43
|
-
# and returns the result.
|
44
|
-
#
|
45
|
-
# If proc is nil, it expects a block to be given and will yield to it.
|
46
|
-
#
|
47
|
-
# @example
|
48
|
-
# Dry::Monads.Right(4).bind(&:succ) # => 5
|
55
|
+
# Apply the second function to value.
|
49
56
|
#
|
50
|
-
# @
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
def
|
57
|
-
|
58
|
-
yield(value, *args)
|
59
|
-
else
|
60
|
-
args[0].call(value, *args.drop(1))
|
61
|
-
end
|
57
|
+
# @api public
|
58
|
+
def either(_, f)
|
59
|
+
f.call(value)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns false
|
63
|
+
def left?
|
64
|
+
false
|
62
65
|
end
|
66
|
+
alias failure? left?
|
67
|
+
|
68
|
+
# Returns true
|
69
|
+
def right?
|
70
|
+
true
|
71
|
+
end
|
72
|
+
alias success? right?
|
63
73
|
|
64
74
|
# Does the same thing as #bind except it also wraps the value
|
65
75
|
# in an instance of Either::Right monad. This allows for easier
|
@@ -68,33 +78,12 @@ module Dry
|
|
68
78
|
# @example
|
69
79
|
# Dry::Monads.Right(4).fmap(&:succ).fmap(->(n) { n**2 }) # => Right(25)
|
70
80
|
#
|
71
|
-
# @param [Array<Object>]
|
81
|
+
# @param args [Array<Object>] arguments will be transparently passed through to #bind
|
72
82
|
# @return [Either::Right]
|
73
83
|
def fmap(*args, &block)
|
74
84
|
Right.new(bind(*args, &block))
|
75
85
|
end
|
76
86
|
|
77
|
-
# Does the same thing as #bind except it returns the original monad
|
78
|
-
# when the result is a Right.
|
79
|
-
#
|
80
|
-
# @example
|
81
|
-
# Dry::Monads.Right(4).tee { Right('ok') } # => Right(4)
|
82
|
-
# Dry::Monads.Right(4).tee { Left('fail') } # => Left('fail')
|
83
|
-
#
|
84
|
-
# @param [Array<Object>] args arguments will be transparently passed through to #bind
|
85
|
-
# @return [Either]
|
86
|
-
def tee(*args, &block)
|
87
|
-
bind(*args, &block).bind { self }
|
88
|
-
end
|
89
|
-
|
90
|
-
# Ignores arguments and returns self. It exists to keep the interface
|
91
|
-
# identical to that of {Either::Left}.
|
92
|
-
#
|
93
|
-
# @return [Either::Right]
|
94
|
-
def or(*)
|
95
|
-
self
|
96
|
-
end
|
97
|
-
|
98
87
|
# @return [String]
|
99
88
|
def to_s
|
100
89
|
"Right(#{value.inspect})"
|
@@ -112,6 +101,8 @@ module Dry
|
|
112
101
|
#
|
113
102
|
# @api public
|
114
103
|
class Left < Either
|
104
|
+
include RightBiased::Left
|
105
|
+
|
115
106
|
alias value left
|
116
107
|
|
117
108
|
# @param left [Object] a value in an error state
|
@@ -119,29 +110,24 @@ module Dry
|
|
119
110
|
@left = left
|
120
111
|
end
|
121
112
|
|
122
|
-
#
|
123
|
-
# identical to that of {Either::Right}.
|
113
|
+
# Apply the first function to value.
|
124
114
|
#
|
125
|
-
# @
|
126
|
-
def
|
127
|
-
|
115
|
+
# @api public
|
116
|
+
def either(f, _)
|
117
|
+
f.call(value)
|
128
118
|
end
|
129
119
|
|
130
|
-
#
|
131
|
-
|
132
|
-
|
133
|
-
# @return [Either::Left]
|
134
|
-
def fmap(*)
|
135
|
-
self
|
120
|
+
# Returns true
|
121
|
+
def left?
|
122
|
+
true
|
136
123
|
end
|
124
|
+
alias failure? left?
|
137
125
|
|
138
|
-
#
|
139
|
-
|
140
|
-
|
141
|
-
# @return [Either::Left]
|
142
|
-
def tee(*)
|
143
|
-
self
|
126
|
+
# Returns false
|
127
|
+
def right?
|
128
|
+
false
|
144
129
|
end
|
130
|
+
alias success? right?
|
145
131
|
|
146
132
|
# If a block is given passes internal value to it and returns the result,
|
147
133
|
# otherwise simply returns the parameter val.
|
@@ -149,7 +135,7 @@ module Dry
|
|
149
135
|
# @example
|
150
136
|
# Dry::Monads.Left(ArgumentError.new('error message')).or(&:message) # => "error message"
|
151
137
|
#
|
152
|
-
# @param [Array<Object>]
|
138
|
+
# @param args [Array<Object>] arguments that will be passed to a block
|
153
139
|
# if one was given, otherwise the first
|
154
140
|
# value will be returned
|
155
141
|
# @return [Object]
|
@@ -161,6 +147,18 @@ module Dry
|
|
161
147
|
end
|
162
148
|
end
|
163
149
|
|
150
|
+
# A lifted version of `#or`. Wraps the passed value or the block result with Either::Right.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
# Dry::Monads.Left.new('no value').or_fmap('value') # => Right("value")
|
154
|
+
# Dry::Monads.Left.new('no value').or_fmap { 'value' } # => Right("value")
|
155
|
+
#
|
156
|
+
# @param args [Array<Object>] arguments will be passed to the underlying `#or` call
|
157
|
+
# @return [Either::Right] Wrapped value
|
158
|
+
def or_fmap(*args, &block)
|
159
|
+
Right.new(self.or(*args, &block))
|
160
|
+
end
|
161
|
+
|
164
162
|
# @return [String]
|
165
163
|
def to_s
|
166
164
|
"Left(#{value.inspect})"
|
@@ -0,0 +1,285 @@
|
|
1
|
+
require 'dry/equalizer'
|
2
|
+
require 'dry/monads/maybe'
|
3
|
+
require 'dry/monads/transformer'
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Monads
|
7
|
+
class List
|
8
|
+
class << self
|
9
|
+
# Builds a list.
|
10
|
+
#
|
11
|
+
# @param values [Array<Object>] List elements
|
12
|
+
# @return [List]
|
13
|
+
def [](*values)
|
14
|
+
new(values)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Coerces a value to a list. `nil` will be coerced to an empty list.
|
18
|
+
#
|
19
|
+
# @param value [Object] Value
|
20
|
+
# @param type [Monad] Embedded monad type (used in case of list of monadic values)
|
21
|
+
# @return [List]
|
22
|
+
def coerce(value, type = nil)
|
23
|
+
if value.nil?
|
24
|
+
List.new([], type)
|
25
|
+
elsif value.respond_to?(:to_ary)
|
26
|
+
List.new(value.to_ary, type)
|
27
|
+
else
|
28
|
+
raise ArgumentError, "Can't coerce #{value.inspect} to List"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Wraps a value with a list.
|
33
|
+
#
|
34
|
+
# @param value [Object] any object
|
35
|
+
# @return [List]
|
36
|
+
def pure(value, type = nil)
|
37
|
+
new([value], type)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
include Dry::Equalizer(:value, :type)
|
42
|
+
include Transformer
|
43
|
+
|
44
|
+
# Internal array value
|
45
|
+
attr_reader :value, :type
|
46
|
+
|
47
|
+
# @api private
|
48
|
+
def initialize(value, type = nil)
|
49
|
+
@value = value
|
50
|
+
@type = type
|
51
|
+
end
|
52
|
+
|
53
|
+
# Lifts a block/proc and runs it against each member of the list.
|
54
|
+
# The block must return a value coercible to a list.
|
55
|
+
# As in other monads if no block given the first argument will
|
56
|
+
# be treated as callable and used instead.
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# Dry::Monads::List[1, 2].bind { |x| [x + 1] } # => List[2, 3]
|
60
|
+
# Dry::Monads::List[1, 2].bind(-> x { [x, x + 1] }) # => List[1, 2, 2, 3]
|
61
|
+
#
|
62
|
+
# @param args [Array<Object>] arguments will be passed to the block or proc
|
63
|
+
# @return [List]
|
64
|
+
def bind(*args)
|
65
|
+
if block_given?
|
66
|
+
List.coerce(value.map { |v| yield(v, *args) }.reduce([], &:+))
|
67
|
+
else
|
68
|
+
obj, *rest = args
|
69
|
+
List.coerce(value.map { |v| obj.(v, *rest) }.reduce([], &:+))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Maps a block over the list. Acts as `Array#map`.
|
74
|
+
# As in other monads if no block given the first argument will
|
75
|
+
# be treated as callable and used instead.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# Dry::Monads::List[1, 2].fmap { |x| x + 1 } # => List[2, 3]
|
79
|
+
#
|
80
|
+
# @param args [Array<Object>] arguments will be passed to the block or proc
|
81
|
+
# @return [List]
|
82
|
+
def fmap(*args)
|
83
|
+
if block_given?
|
84
|
+
List.new(value.map { |v| yield(v, *args) })
|
85
|
+
else
|
86
|
+
obj, *rest = args
|
87
|
+
List.new(value.map { |v| obj.(v, *rest) })
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Maps a block over the list. Acts as `Array#map`.
|
92
|
+
# Requires a block.
|
93
|
+
#
|
94
|
+
# @return [List]
|
95
|
+
def map(&block)
|
96
|
+
if block
|
97
|
+
fmap(block)
|
98
|
+
else
|
99
|
+
raise ArgumentError, "Missing block"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Concatenates two lists.
|
104
|
+
#
|
105
|
+
# @example
|
106
|
+
# Dry::Monads::List[1, 2] + Dry::Monads::List[3, 4] # => List[1, 2, 3, 4]
|
107
|
+
#
|
108
|
+
# @param other [List] Other list
|
109
|
+
# @return [List]
|
110
|
+
def +(other)
|
111
|
+
List.new(to_ary + other.to_ary)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns a string representation of the list.
|
115
|
+
#
|
116
|
+
# @example
|
117
|
+
# Dry::Monads::List[1, 2, 3].inspect # => "List[1, 2, 3]"
|
118
|
+
#
|
119
|
+
# @return [String]
|
120
|
+
def inspect
|
121
|
+
"List#{ value.inspect }"
|
122
|
+
end
|
123
|
+
alias_method :to_s, :inspect
|
124
|
+
|
125
|
+
# Coerces to an array
|
126
|
+
alias_method :to_ary, :value
|
127
|
+
alias_method :to_a, :to_ary
|
128
|
+
|
129
|
+
# Returns the first element.
|
130
|
+
#
|
131
|
+
# @return [Object]
|
132
|
+
def first
|
133
|
+
value.first
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the last element.
|
137
|
+
#
|
138
|
+
# @return [Object]
|
139
|
+
def last
|
140
|
+
value.last
|
141
|
+
end
|
142
|
+
|
143
|
+
# Folds the list from the left.
|
144
|
+
#
|
145
|
+
# @param initial [Object] Initial value
|
146
|
+
# @return [Object]
|
147
|
+
def fold_left(initial)
|
148
|
+
value.reduce(initial) { |acc, v| yield(acc, v) }
|
149
|
+
end
|
150
|
+
alias_method :foldl, :fold_left
|
151
|
+
alias_method :reduce, :fold_left
|
152
|
+
|
153
|
+
# Folds the list from the right.
|
154
|
+
#
|
155
|
+
# @param initial [Object] Initial value
|
156
|
+
# @return [Object]
|
157
|
+
def fold_right(initial)
|
158
|
+
value.reverse.reduce(initial) { |a, b| yield(b, a) }
|
159
|
+
end
|
160
|
+
alias_method :foldr, :fold_right
|
161
|
+
|
162
|
+
# Whether the list is empty.
|
163
|
+
#
|
164
|
+
# @return [TrueClass, FalseClass]
|
165
|
+
def empty?
|
166
|
+
value.empty?
|
167
|
+
end
|
168
|
+
|
169
|
+
# Sorts the list.
|
170
|
+
#
|
171
|
+
# @return [List]
|
172
|
+
def sort
|
173
|
+
coerce(value.sort)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Filters elements with a block
|
177
|
+
#
|
178
|
+
# @return [List]
|
179
|
+
def filter
|
180
|
+
coerce(value.select { |e| yield(e) })
|
181
|
+
end
|
182
|
+
alias_method :select, :filter
|
183
|
+
|
184
|
+
# List size.
|
185
|
+
#
|
186
|
+
# @return [Integer]
|
187
|
+
def size
|
188
|
+
value.size
|
189
|
+
end
|
190
|
+
|
191
|
+
# Reverses the list.
|
192
|
+
#
|
193
|
+
# @return [List]
|
194
|
+
def reverse
|
195
|
+
coerce(value.reverse)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Returns the first element wrapped with a `Maybe`.
|
199
|
+
#
|
200
|
+
# @return [Maybe<Object>]
|
201
|
+
def head
|
202
|
+
Maybe.coerce(value.first)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Returns list's tail.
|
206
|
+
#
|
207
|
+
# @return [List]
|
208
|
+
def tail
|
209
|
+
coerce(value.drop(1))
|
210
|
+
end
|
211
|
+
|
212
|
+
# Turns the list into a types one.
|
213
|
+
# Type is required for some operations like .traverse.
|
214
|
+
#
|
215
|
+
# @param type [Monad] Monad instance
|
216
|
+
# @return [List] Typed list
|
217
|
+
def typed(type = nil)
|
218
|
+
if type.nil?
|
219
|
+
if size.zero?
|
220
|
+
raise ArgumentError, "Cannot infer monad for an empty list"
|
221
|
+
else
|
222
|
+
self.class.new(value, value[0].monad)
|
223
|
+
end
|
224
|
+
else
|
225
|
+
self.class.new(value, type)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Whether the list is types
|
230
|
+
#
|
231
|
+
# @return [Boolean]
|
232
|
+
def typed?
|
233
|
+
!type.nil?
|
234
|
+
end
|
235
|
+
|
236
|
+
# Traverses the list with a block (or without it).
|
237
|
+
# This methods "flips" List structure with the given monad (obtained from the type).
|
238
|
+
# Note that traversing requires the list to be types.
|
239
|
+
# Also if a block given, its returning type must be equal list's type.
|
240
|
+
#
|
241
|
+
# @example
|
242
|
+
# List<Either>[Right(1), Right(2)].traverse # => Right([1, 2])
|
243
|
+
# List<Maybe>[Some(1), None, Some(3)].traverse # => None
|
244
|
+
#
|
245
|
+
# @return [Monad] Result is a monadic value
|
246
|
+
def traverse
|
247
|
+
unless typed?
|
248
|
+
raise StandardError, "Cannot traverse an untyped list"
|
249
|
+
end
|
250
|
+
|
251
|
+
foldl(type.pure(EMPTY)) { |acc, el|
|
252
|
+
acc.bind { |unwrapped|
|
253
|
+
mapped = block_given? ? yield(el) : el
|
254
|
+
mapped.fmap { |i| unwrapped + List[i] }
|
255
|
+
}
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns the List monad.
|
260
|
+
#
|
261
|
+
# @return [Monad]
|
262
|
+
def monad
|
263
|
+
List
|
264
|
+
end
|
265
|
+
|
266
|
+
private
|
267
|
+
|
268
|
+
def coerce(other)
|
269
|
+
self.class.coerce(other)
|
270
|
+
end
|
271
|
+
|
272
|
+
# Empty list
|
273
|
+
EMPTY = List.new([].freeze).freeze
|
274
|
+
|
275
|
+
module Mixin
|
276
|
+
List = List
|
277
|
+
L = List
|
278
|
+
|
279
|
+
def List(value)
|
280
|
+
List.coerce(value)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
data/lib/dry/monads/maybe.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'dry/equalizer'
|
2
2
|
|
3
|
+
require 'dry/monads/right_biased'
|
4
|
+
require 'dry/monads/transformer'
|
5
|
+
|
3
6
|
module Dry
|
4
7
|
module Monads
|
5
8
|
# Represents a value which can exist or not, i.e. it could be nil.
|
@@ -7,15 +10,27 @@ module Dry
|
|
7
10
|
# @api public
|
8
11
|
class Maybe
|
9
12
|
include Dry::Equalizer(:value)
|
13
|
+
include Transformer
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
class << self
|
16
|
+
# Wraps the given value with into a Maybe object.
|
17
|
+
#
|
18
|
+
# @param value [Object] the value to be stored in the monad
|
19
|
+
# @return [Maybe::Some, Maybe::None]
|
20
|
+
def coerce(value)
|
21
|
+
if value.nil?
|
22
|
+
None.instance
|
23
|
+
else
|
24
|
+
Some.new(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
alias_method :lift, :coerce
|
28
|
+
|
29
|
+
# Wraps the given value with `Some`.
|
30
|
+
#
|
31
|
+
# @param value [Object] the value to be stored inside Some
|
32
|
+
# @return [Maybe::Some]
|
33
|
+
def pure(value)
|
19
34
|
Some.new(value)
|
20
35
|
end
|
21
36
|
end
|
@@ -37,10 +52,20 @@ module Dry
|
|
37
52
|
self
|
38
53
|
end
|
39
54
|
|
55
|
+
# Returns the Maybe monad.
|
56
|
+
# This is how we're doing polymorphism in Ruby 😕
|
57
|
+
#
|
58
|
+
# @return [Monad]
|
59
|
+
def monad
|
60
|
+
Maybe
|
61
|
+
end
|
62
|
+
|
40
63
|
# Represents a value that is present, i.e. not nil.
|
41
64
|
#
|
42
65
|
# @api public
|
43
66
|
class Some < Maybe
|
67
|
+
include RightBiased::Right
|
68
|
+
|
44
69
|
attr_reader :value
|
45
70
|
|
46
71
|
def initialize(value)
|
@@ -48,28 +73,6 @@ module Dry
|
|
48
73
|
@value = value
|
49
74
|
end
|
50
75
|
|
51
|
-
# Calls the passed in Proc object with value stored in self
|
52
|
-
# and returns the result.
|
53
|
-
#
|
54
|
-
# If proc is nil, it expects a block to be given and will yield to it.
|
55
|
-
#
|
56
|
-
# @example
|
57
|
-
# Dry::Monads.Some(4).bind(&:succ) # => 5
|
58
|
-
#
|
59
|
-
# @param [Array<Object>] args arguments that will be passed to a block
|
60
|
-
# if one was given, otherwise the first
|
61
|
-
# value assumed to be a Proc (callable)
|
62
|
-
# object and the rest of args will be passed
|
63
|
-
# to this object along with the internal value
|
64
|
-
# @return [Object] result of calling proc or block on the internal value
|
65
|
-
def bind(*args)
|
66
|
-
if block_given?
|
67
|
-
yield(value, *args)
|
68
|
-
else
|
69
|
-
args[0].call(value, *args.drop(1))
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
76
|
# Does the same thing as #bind except it also wraps the value
|
74
77
|
# in an instance of Maybe::Some monad. This allows for easier
|
75
78
|
# chaining of calls.
|
@@ -77,28 +80,13 @@ module Dry
|
|
77
80
|
# @example
|
78
81
|
# Dry::Monads.Some(4).fmap(&:succ).fmap(->(n) { n**2 }) # => Some(25)
|
79
82
|
#
|
80
|
-
# @param [Array<Object>]
|
83
|
+
# @param args [Array<Object>] arguments will be transparently passed through to #bind
|
81
84
|
# @return [Maybe::Some, Maybe::None] Lifted result, i.e. nil will be mapped to None,
|
82
85
|
# other values will be wrapped with Some
|
83
86
|
def fmap(*args, &block)
|
84
87
|
self.class.lift(bind(*args, &block))
|
85
88
|
end
|
86
89
|
|
87
|
-
# Ignores arguments and returns self. It exists to keep the interface
|
88
|
-
# identical to that of {Maybe::None}.
|
89
|
-
#
|
90
|
-
# @return [Maybe::Some]
|
91
|
-
def or(*)
|
92
|
-
self
|
93
|
-
end
|
94
|
-
|
95
|
-
# Returns value. It exists to keep the interface identical to that of {Maybe::None}.
|
96
|
-
#
|
97
|
-
# @return [Object]
|
98
|
-
def value_or(_val = nil)
|
99
|
-
value
|
100
|
-
end
|
101
|
-
|
102
90
|
# @return [String]
|
103
91
|
def to_s
|
104
92
|
"Some(#{value.inspect})"
|
@@ -110,6 +98,8 @@ module Dry
|
|
110
98
|
#
|
111
99
|
# @api public
|
112
100
|
class None < Maybe
|
101
|
+
include RightBiased::Left
|
102
|
+
|
113
103
|
@instance = new
|
114
104
|
singleton_class.send(:attr_reader, :instance)
|
115
105
|
|
@@ -118,22 +108,6 @@ module Dry
|
|
118
108
|
nil
|
119
109
|
end
|
120
110
|
|
121
|
-
# Ignores arguments and returns self. It exists to keep the interface
|
122
|
-
# identical to that of {Maybe::Some}.
|
123
|
-
#
|
124
|
-
# @return [Maybe::None]
|
125
|
-
def bind(*)
|
126
|
-
self
|
127
|
-
end
|
128
|
-
|
129
|
-
# Ignores arguments and returns self. It exists to keep the interface
|
130
|
-
# identical to that of {Maybe::Some}.
|
131
|
-
#
|
132
|
-
# @return [Maybe::None]
|
133
|
-
def fmap(*)
|
134
|
-
self
|
135
|
-
end
|
136
|
-
|
137
111
|
# If a block is given passes internal value to it and returns the result,
|
138
112
|
# otherwise simply returns the parameter val.
|
139
113
|
#
|
@@ -141,7 +115,8 @@ module Dry
|
|
141
115
|
# Dry::Monads.None.or('no value') # => "no value"
|
142
116
|
# Dry::Monads.None.or { Time.now } # => current time
|
143
117
|
#
|
144
|
-
# @param
|
118
|
+
# @param args [Array<Object>] if no block given the first argument will be returned
|
119
|
+
# otherwise arguments will be transparently passed to the block
|
145
120
|
# @return [Object]
|
146
121
|
def or(*args)
|
147
122
|
if block_given?
|
@@ -151,15 +126,18 @@ module Dry
|
|
151
126
|
end
|
152
127
|
end
|
153
128
|
|
154
|
-
#
|
129
|
+
# A lifted version of `#or`. Applies `Maybe.lift` to the passed value or
|
130
|
+
# to the block result.
|
155
131
|
#
|
156
|
-
# @
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
132
|
+
# @example
|
133
|
+
# Dry::Monads.None.or_fmap('no value') # => Some("no value")
|
134
|
+
# Dry::Monads.None.or_fmap { Time.now } # => Some(current time)
|
135
|
+
#
|
136
|
+
# @param args [Array<Object>] arguments will be passed to the underlying `#or` call
|
137
|
+
# @return [Maybe::Some, Maybe::None] Lifted `#or` result, i.e. nil will be mapped to None,
|
138
|
+
# other values will be wrapped with Some
|
139
|
+
def or_fmap(*args, &block)
|
140
|
+
Maybe.lift(self.or(*args, &block))
|
163
141
|
end
|
164
142
|
|
165
143
|
# @return [String]
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Dry
|
2
|
+
module Monads
|
3
|
+
module RightBiased
|
4
|
+
module Right
|
5
|
+
attr_reader :value
|
6
|
+
|
7
|
+
# Calls the passed in Proc object with value stored in self
|
8
|
+
# and returns the result.
|
9
|
+
#
|
10
|
+
# If proc is nil, it expects a block to be given and will yield to it.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# Dry::Monads.Right(4).bind(&:succ) # => 5
|
14
|
+
#
|
15
|
+
# @param [Array<Object>] args arguments that will be passed to a block
|
16
|
+
# if one was given, otherwise the first
|
17
|
+
# value assumed to be a Proc (callable)
|
18
|
+
# object and the rest of args will be passed
|
19
|
+
# to this object along with the internal value
|
20
|
+
# @return [Object] result of calling proc or block on the internal value
|
21
|
+
def bind(*args, **kwargs)
|
22
|
+
vargs, vkwargs = destructure(value)
|
23
|
+
kw = kwargs.empty? && vkwargs.empty? ? [] : [kwargs.merge(vkwargs)]
|
24
|
+
|
25
|
+
if block_given?
|
26
|
+
yield(*vargs, *args, *kw)
|
27
|
+
else
|
28
|
+
obj, *rest = args
|
29
|
+
obj.call(*vargs, *rest, *kw)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Does the same thing as #bind except it returns the original monad
|
34
|
+
# when the result is a Right.
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# Dry::Monads.Right(4).tee { Right('ok') } # => Right(4)
|
38
|
+
# Dry::Monads.Right(4).tee { Left('fail') } # => Left('fail')
|
39
|
+
#
|
40
|
+
# @param [Array<Object>] args arguments will be transparently passed through to #bind
|
41
|
+
# @return [RightBiased::Right]
|
42
|
+
def tee(*args, &block)
|
43
|
+
bind(*args, &block).bind { self }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Abstract method for lifting a block over the monad type
|
47
|
+
# Must be implemented for a right-biased monad
|
48
|
+
#
|
49
|
+
# @return [RightBiased::Right]
|
50
|
+
def fmap(*)
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
# Ignores arguments and returns self. It exists to keep the interface
|
55
|
+
# identical to that of {RightBiased::Left}.
|
56
|
+
#
|
57
|
+
# @return [RightBiased::Right]
|
58
|
+
def or(*)
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# A lifted version of `#or`. For {RightBiased::Right} acts in the same way as `#or`,
|
63
|
+
# that is returns itselt.
|
64
|
+
#
|
65
|
+
# @return [RightBiased::Right]
|
66
|
+
def or_fmap(*)
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns value. It exists to keep the interface identical to that of RightBiased::Left
|
71
|
+
#
|
72
|
+
# @return [Object]
|
73
|
+
def value_or(_val = nil)
|
74
|
+
value
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# @api private
|
80
|
+
def destructure(*args, **kwargs)
|
81
|
+
[args, kwargs]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module Left
|
86
|
+
attr_reader :value
|
87
|
+
|
88
|
+
# Ignores the input parameter and returns self. It exists to keep the interface
|
89
|
+
# identical to that of {RightBiased::Right}.
|
90
|
+
#
|
91
|
+
# @return [RightBiased::Left]
|
92
|
+
def bind(*)
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# Ignores the input parameter and returns self. It exists to keep the interface
|
97
|
+
# identical to that of {RightBiased::Right}.
|
98
|
+
#
|
99
|
+
# @return [RightBiased::Left]
|
100
|
+
def tee(*)
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
# Ignores the input parameter and returns self. It exists to keep the interface
|
105
|
+
# identical to that of {RightBiased::Right}.
|
106
|
+
#
|
107
|
+
# @return [RightBiased::Left]
|
108
|
+
def fmap(*)
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Left-biased #bind version.
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# Dry::Monads.Left(ArgumentError.new('error message')).or(&:message) # => "error message"
|
116
|
+
# Dry::Monads.None.or('no value') # => "no value"
|
117
|
+
# Dry::Monads.None.or { Time.now } # => current time
|
118
|
+
#
|
119
|
+
# @return [Object]
|
120
|
+
def or(*)
|
121
|
+
raise NotImplementedError
|
122
|
+
end
|
123
|
+
|
124
|
+
# A lifted version of `#or`. This is basically `#or` + `#fmap`.
|
125
|
+
#
|
126
|
+
# @example
|
127
|
+
# Dry::Monads.None.or('no value') # => Some("no value")
|
128
|
+
# Dry::Monads.None.or { Time.now } # => Some(current time)
|
129
|
+
#
|
130
|
+
# @return [RightBiased::Left, RightBiased::Right]
|
131
|
+
def or_fmap(*)
|
132
|
+
raise NotImplementedError
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the passed value
|
136
|
+
#
|
137
|
+
# @returns [Object]
|
138
|
+
def value_or(val = nil)
|
139
|
+
if block_given?
|
140
|
+
yield
|
141
|
+
else
|
142
|
+
val
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Dry
|
2
|
+
module Monads
|
3
|
+
module Transformer
|
4
|
+
# Lifts a block/proc over the 2-level nested structure.
|
5
|
+
# This is essentially fmap . fmap (. is the function composition
|
6
|
+
# operator from Haskell) or the functor instance for
|
7
|
+
# a two-level monadic structure like List Either.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# List[Right(1), Left(1)].fmap2 { |x| x + 1 } # => List[Right(2), Left(1)]
|
11
|
+
# Right(None).fmap2 { |x| x + 1 } # => Right(None)
|
12
|
+
#
|
13
|
+
# @param args [Array<Object>] arguments will be passed to the block or the proc
|
14
|
+
# @return [Object] some monadic value
|
15
|
+
def fmap2(*args)
|
16
|
+
if block_given?
|
17
|
+
fmap { |a| a.fmap { |b| yield(b, *args) } }
|
18
|
+
else
|
19
|
+
func, *rest = args
|
20
|
+
fmap { |a| a.fmap { |b| func.(b, *rest) } }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Lifts a block/proc over the 3-level nested structure.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# List[Right(Some(1)), Left(Some(1))].fmap3 { |x| x + 1 } # => List[Right(Some(2)), Left(Some(1))]
|
28
|
+
# Right(None).fmap3 { |x| x + 1 } # => Right(None)
|
29
|
+
#
|
30
|
+
# @param args [Array<Object>] arguments will be passed to the block or the proc
|
31
|
+
# @return [Object] some monadic value
|
32
|
+
def fmap3(*args)
|
33
|
+
if block_given?
|
34
|
+
fmap { |a| a.fmap { |b| b.fmap { |c| yield(c, *args) } } }
|
35
|
+
else
|
36
|
+
func, *rest = args
|
37
|
+
fmap { |a| a.fmap { |b| b.fmap { |c| func.(c, *rest) } } }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/dry/monads/try.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'dry/equalizer'
|
2
2
|
|
3
|
+
require 'dry/monads/right_biased'
|
4
|
+
|
3
5
|
module Dry
|
4
6
|
module Monads
|
5
7
|
# Represents a value which can be either success or a failure (an exception).
|
@@ -7,7 +9,7 @@ module Dry
|
|
7
9
|
#
|
8
10
|
# @api public
|
9
11
|
class Try
|
10
|
-
attr_reader :exception
|
12
|
+
attr_reader :exception
|
11
13
|
|
12
14
|
# Calls the passed in proc object and if successful stores the result in a
|
13
15
|
# {Try::Success} monad, but if one of the specified exceptions was raised it stores
|
@@ -37,6 +39,13 @@ module Dry
|
|
37
39
|
# @api public
|
38
40
|
class Success < Try
|
39
41
|
include Dry::Equalizer(:value, :catchable)
|
42
|
+
include RightBiased::Right
|
43
|
+
|
44
|
+
# Using #or is not a good practice, you should process exceptions
|
45
|
+
# explicitly hence we don't offer an easy way to ignore them.
|
46
|
+
# Use Try#to_maybe if you're sure you need `#or` for `Try`.
|
47
|
+
undef :or
|
48
|
+
|
40
49
|
attr_reader :catchable
|
41
50
|
|
42
51
|
# @param exceptions [Array<Exception>] list of exceptions to be rescued
|
@@ -46,6 +55,9 @@ module Dry
|
|
46
55
|
@value = value
|
47
56
|
end
|
48
57
|
|
58
|
+
alias bind_call bind
|
59
|
+
private :bind_call
|
60
|
+
|
49
61
|
# Calls the passed in Proc object with value stored in self
|
50
62
|
# and returns the result.
|
51
63
|
#
|
@@ -56,18 +68,14 @@ module Dry
|
|
56
68
|
# success.bind(->(n) { n / 2 }) # => 5
|
57
69
|
# success.bind { |n| n / 0 } # => Try::Failure(ZeroDivisionError: divided by 0)
|
58
70
|
#
|
59
|
-
# @param [Array<Object>]
|
71
|
+
# @param args [Array<Object>] arguments that will be passed to a block
|
60
72
|
# if one was given, otherwise the first
|
61
73
|
# value assumed to be a Proc (callable)
|
62
74
|
# object and the rest of args will be passed
|
63
75
|
# to this object along with the internal value
|
64
76
|
# @return [Object, Try::Failure]
|
65
|
-
def bind(*
|
66
|
-
|
67
|
-
yield(value, *args)
|
68
|
-
else
|
69
|
-
args[0].call(value, *args.drop(1))
|
70
|
-
end
|
77
|
+
def bind(*)
|
78
|
+
super
|
71
79
|
rescue *catchable => e
|
72
80
|
Failure.new(e)
|
73
81
|
end
|
@@ -81,15 +89,13 @@ module Dry
|
|
81
89
|
# success.fmap(&:succ).fmap(&:succ).value # => 12
|
82
90
|
# success.fmap(&:succ).fmap { |n| n / 0 }.fmap(&:succ).value # => nil
|
83
91
|
#
|
84
|
-
# @param [Array<Object>]
|
92
|
+
# @param args [Array<Object>] extra arguments for the block, arguments are being processes
|
85
93
|
# just as in #bind
|
86
94
|
# @return [Try::Success, Try::Failure]
|
87
95
|
def fmap(*args, &block)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
Try.lift(catchable, -> { args[0].call(value, *args.drop(1)) })
|
92
|
-
end
|
96
|
+
Success.new(catchable, bind_call(*args, &block))
|
97
|
+
rescue *catchable => e
|
98
|
+
Failure.new(e)
|
93
99
|
end
|
94
100
|
|
95
101
|
# @return [Maybe]
|
@@ -114,28 +120,14 @@ module Dry
|
|
114
120
|
# @api public
|
115
121
|
class Failure < Try
|
116
122
|
include Dry::Equalizer(:exception)
|
123
|
+
include RightBiased::Left
|
124
|
+
undef :or
|
117
125
|
|
118
126
|
# @param exception [Exception]
|
119
127
|
def initialize(exception)
|
120
128
|
@exception = exception
|
121
129
|
end
|
122
130
|
|
123
|
-
# Ignores arguments and returns self. It exists to keep the interface
|
124
|
-
# identical to that of {Try::Success}.
|
125
|
-
#
|
126
|
-
# @return [Try::Failure]
|
127
|
-
def bind(*)
|
128
|
-
self
|
129
|
-
end
|
130
|
-
|
131
|
-
# Ignores arguments and returns self. It exists to keep the interface
|
132
|
-
# identical to that of {Try::Success}.
|
133
|
-
#
|
134
|
-
# @return [Try::Failure]
|
135
|
-
def fmap(*)
|
136
|
-
self
|
137
|
-
end
|
138
|
-
|
139
131
|
# @return [Maybe::None]
|
140
132
|
def to_maybe
|
141
133
|
Dry::Monads::None()
|
@@ -171,12 +163,14 @@ module Dry
|
|
171
163
|
module Mixin
|
172
164
|
Try = Try
|
173
165
|
|
166
|
+
DEFAULT_EXCEPTIONS = [StandardError].freeze
|
167
|
+
|
174
168
|
# A convenience wrapper for {Try.lift}.
|
175
169
|
# If no exceptions are provided it falls back to StandardError.
|
176
170
|
# In general, relying on this behaviour is not recommended as it can lead to unnoticed
|
177
171
|
# bugs and it is always better to explicitly specify a list of exceptions if possible.
|
178
172
|
def Try(*exceptions, &f)
|
179
|
-
catchable = exceptions.
|
173
|
+
catchable = exceptions.empty? ? DEFAULT_EXCEPTIONS : exceptions.flatten
|
180
174
|
Try.lift(catchable, f)
|
181
175
|
end
|
182
176
|
end
|
data/lib/dry/monads/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-monads
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.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:
|
11
|
+
date: 2017-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-equalizer
|
@@ -76,9 +76,8 @@ files:
|
|
76
76
|
- ".codeclimate.yml"
|
77
77
|
- ".gitignore"
|
78
78
|
- ".rspec"
|
79
|
-
- ".rubocop.yml"
|
80
|
-
- ".rubocop_todo.yml"
|
81
79
|
- ".travis.yml"
|
80
|
+
- CHANGELOG.md
|
82
81
|
- Gemfile
|
83
82
|
- LICENSE
|
84
83
|
- README.md
|
@@ -89,7 +88,10 @@ files:
|
|
89
88
|
- lib/dry-monads.rb
|
90
89
|
- lib/dry/monads.rb
|
91
90
|
- lib/dry/monads/either.rb
|
91
|
+
- lib/dry/monads/list.rb
|
92
92
|
- lib/dry/monads/maybe.rb
|
93
|
+
- lib/dry/monads/right_biased.rb
|
94
|
+
- lib/dry/monads/transformer.rb
|
93
95
|
- lib/dry/monads/try.rb
|
94
96
|
- lib/dry/monads/version.rb
|
95
97
|
- lib/json/add/dry/monads/maybe.rb
|
@@ -114,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
116
|
version: '0'
|
115
117
|
requirements: []
|
116
118
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.
|
119
|
+
rubygems_version: 2.6.10
|
118
120
|
signing_key:
|
119
121
|
specification_version: 4
|
120
122
|
summary: Common monads for Ruby.
|
data/.rubocop.yml
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
inherit_from: .rubocop_todo.yml
|
2
|
-
|
3
|
-
AllCops:
|
4
|
-
Exclude:
|
5
|
-
- 'spec/integration/either_spec.rb'
|
6
|
-
- 'vendor/**/*'
|
7
|
-
|
8
|
-
Style/Documentation:
|
9
|
-
Enabled: false
|
10
|
-
|
11
|
-
Style/StringLiterals:
|
12
|
-
Enabled: false
|
13
|
-
|
14
|
-
Style/MethodName:
|
15
|
-
Enabled: false
|
16
|
-
|
17
|
-
Style/LambdaCall:
|
18
|
-
Enabled: false
|
19
|
-
|
20
|
-
Style/StabbyLambdaParentheses:
|
21
|
-
Enabled: false
|
22
|
-
|
23
|
-
Style/NestedParenthesizedCalls:
|
24
|
-
Enabled: false
|
25
|
-
|
26
|
-
Style/MultilineBlockChain:
|
27
|
-
Enabled: false
|
28
|
-
|
29
|
-
Style/GuardClause:
|
30
|
-
Enabled: false
|
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
|
-
|
47
|
-
Metrics/LineLength:
|
48
|
-
Max: 110
|
49
|
-
|
50
|
-
Style/FileName:
|
51
|
-
Exclude:
|
52
|
-
- 'lib/dry-monads.rb'
|
53
|
-
|
54
|
-
Lint/Debugger:
|
55
|
-
Exclude:
|
56
|
-
- 'bin/console'
|
57
|
-
|
58
|
-
Lint/HandleExceptions:
|
59
|
-
Exclude:
|
60
|
-
- 'spec/spec_helper.rb'
|
61
|
-
|
62
|
-
Security/JSONLoad:
|
63
|
-
Exclude:
|
64
|
-
- 'spec/**/*'
|
data/.rubocop_todo.yml
DELETED
@@ -1,7 +0,0 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
# `rubocop --auto-gen-config`
|
3
|
-
# on 2016-04-29 01:33:48 +0100 using RuboCop version 0.39.0.
|
4
|
-
# The point is for the user to remove these configuration records
|
5
|
-
# one by one as the offenses are removed from the code base.
|
6
|
-
# Note that changes in the inspected code, or installation of new
|
7
|
-
# versions of RuboCop, may require this file to be generated again.
|