deterministic 0.1.0 → 0.1.1
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/README.md +70 -22
- data/lib/deterministic/either/attempt_all.rb +3 -2
- data/lib/deterministic/either/failure.rb +1 -1
- data/lib/deterministic/either/match.rb +44 -21
- data/lib/deterministic/either/success.rb +1 -1
- data/lib/deterministic/either.rb +6 -37
- data/lib/deterministic/monad.rb +49 -0
- data/lib/deterministic/version.rb +1 -1
- data/lib/deterministic.rb +2 -1
- data/spec/lib/deterministic/attempt_all_spec.rb +0 -1
- data/spec/lib/deterministic/either/match_spec.rb +18 -2
- data/spec/lib/deterministic/either/success_spec.rb +20 -12
- data/spec/lib/deterministic/monad_axioms.rb +48 -0
- data/spec/lib/deterministic/monad_spec.rb +29 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 718affa7e1715d28293a98600c6112c800d32d3a
|
4
|
+
data.tar.gz: 600f1c5da38df13e625da735485af9e373a240e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe73aa1b7315083c0ccfc507b240ef981293f2d8e3ceb89dc02ad3386ca5e14286b7fd944f911feb1e5d7ba9874fb364071e293062b77ef1172e9e43af334172
|
7
|
+
data.tar.gz: 6070733d16a169a9e2b4b7685b90805a5758fb5146ffc9803a26ce58793a24ce93f4711332ae320aac3a81bec51cd8b530ddcc3be93dbfd068bd29f10d13f33c
|
data/README.md
CHANGED
@@ -1,20 +1,8 @@
|
|
1
1
|
# Deterministic
|
2
2
|
|
3
|
-
This is a spiritual successor of the [Monadic gem](http://github.com/pzol/monadic)
|
3
|
+
This is a spiritual successor of the [Monadic gem](http://github.com/pzol/monadic).
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
Add this line to your application's Gemfile:
|
8
|
-
|
9
|
-
gem 'deterministic'
|
10
|
-
|
11
|
-
And then execute:
|
12
|
-
|
13
|
-
$ bundle
|
14
|
-
|
15
|
-
Or install it yourself as:
|
16
|
-
|
17
|
-
$ gem install deterministic
|
5
|
+
This gem is still __WORK IN PROGRESS__.
|
18
6
|
|
19
7
|
## Usage
|
20
8
|
|
@@ -80,11 +68,76 @@ Success(1).match do
|
|
80
68
|
either { |v| "either #{v}"}
|
81
69
|
end # => "either 1"
|
82
70
|
```
|
83
|
-
|
71
|
+
Note1: the inner value has been unwrapped!
|
72
|
+
|
73
|
+
Note2: only the __last__ matching pattern block will be executed, so order __can__ be important.
|
74
|
+
|
75
|
+
The result returned will be the result of the __last__ `#try` or `#let`. As a side note, `#try` is a monad, `#let` is a functor.
|
76
|
+
|
77
|
+
Values for patterns are good, too:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
Success(1).match do
|
81
|
+
success(1) { "Success #{v}" }
|
82
|
+
end # => "Success 1"
|
83
|
+
```
|
84
|
+
|
85
|
+
You can and should also use procs for patterns:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
Success(1).match do
|
89
|
+
success ->(v) { v == 1} { "Success #{v}" }
|
90
|
+
end # => "Success 1"
|
91
|
+
```
|
92
|
+
|
93
|
+
Combining `#attempt_all` and `#match` is the ultimate sophistication:
|
84
94
|
|
85
|
-
|
95
|
+
```ruby
|
96
|
+
Either.attempt_all do
|
97
|
+
try { 1 }
|
98
|
+
try { |v| v + 1 }
|
99
|
+
end.match do
|
100
|
+
success(1) { |v| "We made it to step #{v}" }
|
101
|
+
success(2) { |v| "The correct answer is #{v}"}
|
102
|
+
end # => "The correct answer is 2"
|
103
|
+
```
|
86
104
|
|
87
|
-
|
105
|
+
If no match was found a `NoMatchError` is raised, so make sure you always cover all possible outcomes.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
Success(1).match do
|
109
|
+
failure(1) { "you'll never get me" }
|
110
|
+
end # => NoMatchError
|
111
|
+
```
|
112
|
+
|
113
|
+
A way to have a catch-all would be using an `any`:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
Success(1).match do
|
117
|
+
any { "catch-all" }
|
118
|
+
end # => "catch-all"
|
119
|
+
```
|
120
|
+
|
121
|
+
## Inspirations
|
122
|
+
* My [Monadic gem](http://github.com/pzol/monadic) of course
|
123
|
+
* `#attempt_all` was somewhat inspired by [An error monad in Clojure](http://brehaut.net/blog/2011/error_monads)
|
124
|
+
* [Pithyless' rumblings](https://gist.github.com/pithyless/2216519)
|
125
|
+
* [either by rsslldnphy](https://github.com/rsslldnphy/either)
|
126
|
+
* [Functors, Applicatives, And Monads In Pictures](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html)
|
127
|
+
|
128
|
+
## Installation
|
129
|
+
|
130
|
+
Add this line to your application's Gemfile:
|
131
|
+
|
132
|
+
gem 'deterministic'
|
133
|
+
|
134
|
+
And then execute:
|
135
|
+
|
136
|
+
$ bundle
|
137
|
+
|
138
|
+
Or install it yourself as:
|
139
|
+
|
140
|
+
$ gem install deterministic
|
88
141
|
|
89
142
|
## Contributing
|
90
143
|
|
@@ -93,8 +146,3 @@ The result returned will be the result of the last `#try` or `#let`
|
|
93
146
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
94
147
|
4. Push to the branch (`git push origin my-new-feature`)
|
95
148
|
5. Create new Pull Request
|
96
|
-
|
97
|
-
## Inspirations
|
98
|
-
* My [Monadic gem](http://github.com/pzol/monadic) of course
|
99
|
-
* `#attempt_all` was somewhat inspired by [An error monad in Clojure](http://brehaut.net/blog/2011/error_monads)
|
100
|
-
* [Pithyless' rumblings](https://gist.github.com/pithyless/2216519)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'ostruct'
|
2
|
-
|
2
|
+
|
3
|
+
class Deterministic::Either
|
3
4
|
def self.attempt_all(context=OpenStruct.new, &block)
|
4
5
|
AttemptAll.new(context, &block).call
|
5
6
|
end
|
@@ -14,7 +15,7 @@ module Deterministic::Either
|
|
14
15
|
|
15
16
|
def call(initial=nil)
|
16
17
|
result = @tries.inject(Success(initial)) do |acc, try|
|
17
|
-
acc.success? ? acc
|
18
|
+
acc.success? ? acc << try.call(acc) : acc
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -1,44 +1,67 @@
|
|
1
|
-
module Deterministic::
|
1
|
+
module Deterministic::PatternMatching
|
2
|
+
|
2
3
|
def match(proc=nil, &block)
|
3
4
|
match = Match.new(self)
|
4
5
|
match.instance_eval &(proc || block)
|
5
6
|
match.result
|
6
7
|
end
|
7
8
|
|
9
|
+
class NoMatchError < StandardError; end
|
10
|
+
|
8
11
|
class Match
|
9
|
-
def initialize(
|
10
|
-
@
|
12
|
+
def initialize(container)
|
13
|
+
@container = container
|
11
14
|
@collection = []
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
15
|
-
|
17
|
+
def result
|
18
|
+
matcher = @collection.select { |m| m.matches?(@container.value) }.last
|
19
|
+
raise NoMatchError if matcher.nil?
|
20
|
+
matcher.result(@container.value)
|
16
21
|
end
|
17
22
|
|
18
|
-
|
19
|
-
|
23
|
+
# TODO: Either specific DSL, will need to move it to Either, later on
|
24
|
+
%w[Success Failure Either].each do |s|
|
25
|
+
define_method s.downcase.to_sym do |value=nil, &block|
|
26
|
+
klas = Module.const_get(s)
|
27
|
+
push(klas, value, block)
|
28
|
+
end
|
20
29
|
end
|
21
30
|
|
22
|
-
|
23
|
-
|
31
|
+
# catch-all
|
32
|
+
def any(value=nil, &result_block)
|
33
|
+
push(Object, value, result_block)
|
24
34
|
end
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
36
|
+
private
|
37
|
+
Matcher = Struct.new(:condition, :block) do
|
38
|
+
def matches?(value)
|
39
|
+
condition.call(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def result(value)
|
43
|
+
block.call(value)
|
44
|
+
end
|
29
45
|
end
|
30
46
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
condition_p = condition
|
37
|
-
else
|
38
|
-
condition_p = ->(v) { condition == @either.value }
|
47
|
+
def push(type, condition, result_block)
|
48
|
+
condition_pred = case
|
49
|
+
when condition.nil?; ->(v) { true }
|
50
|
+
when condition.is_a?(Proc); condition
|
51
|
+
else ->(v) { condition == @container.value }
|
39
52
|
end
|
40
53
|
|
41
|
-
|
54
|
+
matcher_pred = compose_predicates(type_pred[type], condition_pred)
|
55
|
+
@collection << Matcher.new(matcher_pred, result_block)
|
56
|
+
end
|
57
|
+
|
58
|
+
def compose_predicates(f, g)
|
59
|
+
->(*args) { f[*args] && g[*args] }
|
60
|
+
end
|
61
|
+
|
62
|
+
# return a partial function for matching a matcher's type
|
63
|
+
def type_pred
|
64
|
+
(->(type, x) { @container.is_a? type }).curry
|
42
65
|
end
|
43
66
|
end
|
44
67
|
end
|
data/lib/deterministic/either.rb
CHANGED
@@ -1,24 +1,15 @@
|
|
1
|
-
module Deterministic
|
1
|
+
module Deterministic
|
2
2
|
def Success(value)
|
3
|
-
Success.
|
3
|
+
Success.new(value)
|
4
4
|
end
|
5
5
|
|
6
6
|
def Failure(value)
|
7
|
-
Failure.
|
7
|
+
Failure.new(value)
|
8
8
|
end
|
9
9
|
|
10
10
|
class Either
|
11
|
-
|
12
|
-
|
13
|
-
# return Failure.new(value) if value.nil? || (value.respond_to?(:empty?) && value.empty?) || !value
|
14
|
-
# return Success.new(value)
|
15
|
-
return new(value)
|
16
|
-
end
|
17
|
-
|
18
|
-
def is?(s)
|
19
|
-
const_name = s.slice(0,1).capitalize + s.slice(1..-1)
|
20
|
-
is_a? Module.const_get(const_name)
|
21
|
-
end
|
11
|
+
include Monad
|
12
|
+
include Deterministic::PatternMatching
|
22
13
|
|
23
14
|
def success?
|
24
15
|
is_a? Success
|
@@ -28,31 +19,9 @@ module Deterministic::Either
|
|
28
19
|
is_a? Failure
|
29
20
|
end
|
30
21
|
|
31
|
-
def
|
22
|
+
def <<(other)
|
32
23
|
return self if failure?
|
33
24
|
return other if other.is_a? Either
|
34
|
-
# return concat(proc) if proc.is_a? Either
|
35
|
-
|
36
|
-
# begin
|
37
|
-
# Either(call(proc, block))
|
38
|
-
# rescue StandardError => error
|
39
|
-
# Failure(error)
|
40
|
-
# end
|
41
|
-
end
|
42
|
-
|
43
|
-
# get the underlying value
|
44
|
-
def value
|
45
|
-
@value
|
46
|
-
end
|
47
|
-
|
48
|
-
def ==(other)
|
49
|
-
return false unless other.is_a? self.class
|
50
|
-
@value == other.instance_variable_get(:@value)
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
def initialize(value)
|
55
|
-
@value = value
|
56
25
|
end
|
57
26
|
end
|
58
27
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Deterministic
|
2
|
+
module Monad
|
3
|
+
class NotMonadError < StandardError; end
|
4
|
+
|
5
|
+
def initialize(value)
|
6
|
+
@value = join(value)
|
7
|
+
end
|
8
|
+
|
9
|
+
# If the passed value is monad already, get the value to avoid nesting
|
10
|
+
# M[M[A]] is equivalent to M[A]
|
11
|
+
def join(value)
|
12
|
+
if value.is_a? self.class then value.value
|
13
|
+
else value end
|
14
|
+
end
|
15
|
+
|
16
|
+
# The functor: takes a function (a -> b) and applies it to the inner value of the monad (Ma),
|
17
|
+
# boxes it back to the same monad (Mb)
|
18
|
+
# fmap :: (a -> b) -> Ma -> Mb
|
19
|
+
def map(proc=nil, &block)
|
20
|
+
result = (proc || block).call(value)
|
21
|
+
self.class.new(result)
|
22
|
+
end
|
23
|
+
|
24
|
+
# The monad: takes a function which returns a monad, applies
|
25
|
+
# bind :: Ma -> (a -> Mb) -> Mb
|
26
|
+
def bind(proc=nil, &block)
|
27
|
+
result = (proc || block).call(value)
|
28
|
+
raise NotMonadError unless result.is_a? Monad
|
29
|
+
self.class.new(result)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get the underlying value, return in Haskell
|
33
|
+
# return :: m a -> a
|
34
|
+
def value
|
35
|
+
@value
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(other)
|
39
|
+
return false unless other.is_a? self.class
|
40
|
+
@value == other.instance_variable_get(:@value)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return the string representation of the Monad
|
44
|
+
def to_s
|
45
|
+
pretty_class_name = self.class.name.split('::')[-1]
|
46
|
+
"#{pretty_class_name}(#{self.value.inspect})"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/deterministic.rb
CHANGED
@@ -2,8 +2,9 @@ require "deterministic/version"
|
|
2
2
|
|
3
3
|
module Deterministic; end
|
4
4
|
|
5
|
-
require 'deterministic/
|
5
|
+
require 'deterministic/monad'
|
6
6
|
require 'deterministic/either/match'
|
7
|
+
require 'deterministic/either'
|
7
8
|
require 'deterministic/either/attempt_all'
|
8
9
|
require 'deterministic/either/success'
|
9
10
|
require 'deterministic/either/failure'
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'deterministic'
|
3
3
|
|
4
|
-
include Deterministic
|
4
|
+
include Deterministic
|
5
5
|
|
6
6
|
describe Deterministic::Either::Match do
|
7
7
|
it "can match Success" do
|
@@ -34,6 +34,14 @@ describe Deterministic::Either::Match do
|
|
34
34
|
).to eq "matched 2"
|
35
35
|
end
|
36
36
|
|
37
|
+
it "catch-all" do
|
38
|
+
expect(
|
39
|
+
Success(1).match do
|
40
|
+
any { "catch-all" }
|
41
|
+
end
|
42
|
+
).to eq "catch-all"
|
43
|
+
end
|
44
|
+
|
37
45
|
it "can match either" do
|
38
46
|
expect(
|
39
47
|
Failure(2).match do
|
@@ -44,11 +52,19 @@ describe Deterministic::Either::Match do
|
|
44
52
|
).to eq "either 2"
|
45
53
|
end
|
46
54
|
|
47
|
-
it "can
|
55
|
+
it "can match with lambdas" do
|
48
56
|
expect(
|
49
57
|
Success(1).match do
|
50
58
|
success ->(v) { v == 1 } { |v| "matched #{v}" }
|
51
59
|
end
|
52
60
|
).to eq "matched 1"
|
53
61
|
end
|
62
|
+
|
63
|
+
it "no match" do
|
64
|
+
expect {
|
65
|
+
Success(1).match do
|
66
|
+
failure { "you'll never get me" }
|
67
|
+
end
|
68
|
+
}.to raise_error Deterministic::PatternMatching::NoMatchError
|
69
|
+
end
|
54
70
|
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require_relative '../monad_axioms'
|
2
3
|
require 'deterministic'
|
3
4
|
|
4
|
-
include Deterministic
|
5
|
+
include Deterministic
|
5
6
|
|
6
|
-
describe Deterministic::
|
7
|
-
|
7
|
+
describe Deterministic::Success do
|
8
|
+
|
9
|
+
it_behaves_like 'a Monad' do
|
10
|
+
let(:monad) { described_class }
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { described_class.new(1) }
|
8
14
|
|
9
15
|
specify { expect(subject).to be_an_instance_of described_class }
|
10
16
|
specify { expect(subject.value).to eq 1 }
|
@@ -12,16 +18,18 @@ describe Deterministic::Either::Success do
|
|
12
18
|
specify { expect(subject).not_to be_failure }
|
13
19
|
|
14
20
|
# public constructor #Success[]
|
15
|
-
specify { expect(
|
16
|
-
specify { expect(subject).to eq(described_class.
|
21
|
+
specify { expect(subject).to be_an_instance_of described_class }
|
22
|
+
specify { expect(subject).to eq(described_class.new(1)) }
|
23
|
+
specify { expect(subject << Success(2)).to eq(Success(2)) }
|
24
|
+
specify { expect(subject << Failure(2)).to eq(Failure(2)) }
|
25
|
+
specify { expect(Success(subject)).to eq Success(1) }
|
26
|
+
specify { expect(subject.map { |v| v + 1} ).to eq Success(2) }
|
17
27
|
|
18
|
-
it "
|
19
|
-
expect(
|
20
|
-
|
28
|
+
it "#bind" do
|
29
|
+
expect(
|
30
|
+
subject.bind { |v| true ? Success(v + 1) : Failure(v + 2)}
|
31
|
+
).to eq Success(2)
|
21
32
|
end
|
22
33
|
|
23
|
-
specify { expect
|
24
|
-
specify { expect(Success(1).is? :success).to be true }
|
25
|
-
specify { expect(Success(1).is? :either).to be true }
|
26
|
-
specify { expect(Success(1).is? :failure).to be false }
|
34
|
+
specify { expect { Success("a").bind(&:upcase) }.to raise_error(Deterministic::Monad::NotMonadError) }
|
27
35
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
shared_examples 'a Monad' do
|
2
|
+
describe 'axioms' do
|
3
|
+
it '1st monadic law: left-identity' do
|
4
|
+
f = ->(value) { monad.new(value + 1) }
|
5
|
+
expect(
|
6
|
+
monad::new(1).bind do |value|
|
7
|
+
f.(value)
|
8
|
+
end
|
9
|
+
).to eq f.(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
it '2nd monadic law: right-identy - new and bind do not change the value' do
|
13
|
+
expect(
|
14
|
+
monad.new(1).bind do |value|
|
15
|
+
monad.new(value)
|
16
|
+
end
|
17
|
+
).to eq monad.new(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it '3rd monadic law: associativity' do
|
21
|
+
f = ->(value) { monad.new(value + 1) }
|
22
|
+
g = ->(value) { monad.new(value + 100) }
|
23
|
+
|
24
|
+
id1 = monad.new(1).bind do |a|
|
25
|
+
f.(a)
|
26
|
+
end.bind do |b|
|
27
|
+
g.(b)
|
28
|
+
end
|
29
|
+
|
30
|
+
id2 = monad.new(1).bind do |a|
|
31
|
+
f.(a).bind do |b|
|
32
|
+
g.(b)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
expect(id1).to eq id2
|
37
|
+
end
|
38
|
+
|
39
|
+
it '#bind must return a monad' do
|
40
|
+
expect(monad.new(1).bind { |v| monad.new(v) }).to eq monad.new(1)
|
41
|
+
expect { monad.new(1).bind {} }.to raise_error(Deterministic::Monad::NotMonadError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it '#new must return a monad' do
|
45
|
+
expect(monad.new(1)).to be_a monad
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative 'monad_axioms'
|
3
|
+
require 'deterministic'
|
4
|
+
|
5
|
+
|
6
|
+
describe Deterministic::Monad do
|
7
|
+
class Identity
|
8
|
+
include Deterministic::Monad
|
9
|
+
end
|
10
|
+
|
11
|
+
it_behaves_like 'a Monad' do
|
12
|
+
let(:monad) { Identity }
|
13
|
+
end
|
14
|
+
|
15
|
+
specify { expect(Identity.new(1).to_s).to eq 'Identity(1)' }
|
16
|
+
specify { expect(Identity.new(nil).to_s).to eq 'Identity(nil)' }
|
17
|
+
specify { expect(Identity.new([1, 2]).map(&:to_s)).to eq Identity.new("[1, 2]") }
|
18
|
+
specify { expect(Identity.new(1).map {|v| v + 2}).to eq Identity.new(3) }
|
19
|
+
specify { expect(Identity.new('foo').map(&:upcase)).to eq Identity.new('FOO')}
|
20
|
+
specify { expect { Identity.new(1).bind {} }.to raise_error(Deterministic::Monad::NotMonadError) }
|
21
|
+
specify { expect(Identity.new(Identity.new(1))).to eq Identity.new(1) }
|
22
|
+
|
23
|
+
# it 'delegates #flat_map to an underlying collection and wraps the resulting collection' do
|
24
|
+
# Identity.unit([1,2]).flat_map {|v| v + 1}.should == Identity.unit([2, 3])
|
25
|
+
# Identity.unit(['foo', 'bar']).flat_map(&:upcase).should == Identity.unit(['FOO', 'BAR'])
|
26
|
+
# expect { Identity.unit(1).flat_map {|v| v + 1 } }.to raise_error(RuntimeError)
|
27
|
+
# end
|
28
|
+
|
29
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deterministic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Zolnierek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -130,10 +130,13 @@ files:
|
|
130
130
|
- lib/deterministic/either/failure.rb
|
131
131
|
- lib/deterministic/either/match.rb
|
132
132
|
- lib/deterministic/either/success.rb
|
133
|
+
- lib/deterministic/monad.rb
|
133
134
|
- lib/deterministic/version.rb
|
134
135
|
- spec/lib/deterministic/attempt_all_spec.rb
|
135
136
|
- spec/lib/deterministic/either/match_spec.rb
|
136
137
|
- spec/lib/deterministic/either/success_spec.rb
|
138
|
+
- spec/lib/deterministic/monad_axioms.rb
|
139
|
+
- spec/lib/deterministic/monad_spec.rb
|
137
140
|
- spec/spec_helper.rb
|
138
141
|
homepage: http://github.com/pzol/deterministic
|
139
142
|
licenses:
|
@@ -163,4 +166,6 @@ test_files:
|
|
163
166
|
- spec/lib/deterministic/attempt_all_spec.rb
|
164
167
|
- spec/lib/deterministic/either/match_spec.rb
|
165
168
|
- spec/lib/deterministic/either/success_spec.rb
|
169
|
+
- spec/lib/deterministic/monad_axioms.rb
|
170
|
+
- spec/lib/deterministic/monad_spec.rb
|
166
171
|
- spec/spec_helper.rb
|