unsound 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/unsound/data.rb +2 -101
- data/lib/unsound/data/either.rb +101 -0
- data/lib/unsound/data/maybe.rb +87 -0
- data/lib/unsound/version.rb +1 -1
- data/spec/unit/unsound/data/maybe_spec.rb +92 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c645e777dc983c390c247a59c5f2de02727346b
|
4
|
+
data.tar.gz: a316ea5ccba27d63f3a823344e7c256f36056f1c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c2101d6baca2654803b9edcca62d5f2e7967a29114f252a7e1c7dbef763ffaa98c0bcaa00e042cf524014a04f13c46de7a7962f43e1857792a24f5921f221c0
|
7
|
+
data.tar.gz: 59979f8819eff54c06cab5d71eb3a8e27e2e555b1185a6721df4235d1512bd443affd17ed3a0a1adea2b6ae7609442ae0bf9e6feddd12c88e69cdc29e40477de
|
data/lib/unsound/data.rb
CHANGED
@@ -1,101 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
module Unsound
|
5
|
-
module Data
|
6
|
-
# @abstract
|
7
|
-
class Either
|
8
|
-
include AbstractType
|
9
|
-
include Concord::Public.new(:value)
|
10
|
-
|
11
|
-
# Wraps a raw value in a {Data::Right}
|
12
|
-
#
|
13
|
-
# @param value [Any] the value to wrap
|
14
|
-
# @return [Data::Right]
|
15
|
-
def self.of(value)
|
16
|
-
Right.new(value)
|
17
|
-
end
|
18
|
-
|
19
|
-
abstract_method :fmap
|
20
|
-
abstract_method :>>
|
21
|
-
|
22
|
-
abstract_method :either
|
23
|
-
abstract_method :and_then
|
24
|
-
abstract_method :or_else
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
def of(value)
|
29
|
-
self.class.of(value)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Left < Either
|
34
|
-
# A Noop
|
35
|
-
#
|
36
|
-
# @return [Data::Left]
|
37
|
-
def fmap(*)
|
38
|
-
self
|
39
|
-
end
|
40
|
-
|
41
|
-
# A Noop
|
42
|
-
#
|
43
|
-
# @return [Data::Left]
|
44
|
-
def >>(*)
|
45
|
-
self
|
46
|
-
end
|
47
|
-
alias :and_then :>>
|
48
|
-
|
49
|
-
# Chain another operation which can result in a {Data::Either}
|
50
|
-
#
|
51
|
-
# @param f[#call] the next operation
|
52
|
-
# @return [Data::Left, Data::Right]
|
53
|
-
def or_else(f = nil, &blk)
|
54
|
-
(f || blk)[value]
|
55
|
-
end
|
56
|
-
|
57
|
-
# Call a function on the value in the {Data::Left}
|
58
|
-
#
|
59
|
-
# @param f [#call] a function capable of processing the value
|
60
|
-
# @param _ [#call] a function that will never be called
|
61
|
-
def either(f, _)
|
62
|
-
f[value]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
class Right < Either
|
67
|
-
# Apply a function to a value contained in a {Data::Right}
|
68
|
-
#
|
69
|
-
# @param f [#call] the function to apply
|
70
|
-
# @return [Data::Right] the result of applying the function
|
71
|
-
# wrapped in a {Data::Right}
|
72
|
-
def fmap(f = nil, &blk)
|
73
|
-
self >> Composition.compose(method(:of), (f || blk))
|
74
|
-
end
|
75
|
-
|
76
|
-
# Chain another operation which can result in a {Data::Either}
|
77
|
-
#
|
78
|
-
# @param f[#call] the next operation
|
79
|
-
# @return [Data::Left, Data::Right]
|
80
|
-
def >>(f = nil, &blk)
|
81
|
-
(f || blk)[value]
|
82
|
-
end
|
83
|
-
alias :and_then :>>
|
84
|
-
|
85
|
-
# A Noop
|
86
|
-
#
|
87
|
-
# @return [Data::Right]
|
88
|
-
def or_else(*)
|
89
|
-
self
|
90
|
-
end
|
91
|
-
|
92
|
-
# Call a function on the value in the {Data::Right}
|
93
|
-
#
|
94
|
-
# @param _ [#call] a function that will never be called
|
95
|
-
# @param f [#call] a function capable of processing the value
|
96
|
-
def either(_, f)
|
97
|
-
f[value]
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
1
|
+
require_relative "data/either"
|
2
|
+
require_relative "data/maybe"
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "abstract_type"
|
2
|
+
require "concord"
|
3
|
+
|
4
|
+
module Unsound
|
5
|
+
module Data
|
6
|
+
# @abstract
|
7
|
+
class Either
|
8
|
+
include AbstractType
|
9
|
+
include Concord::Public.new(:value)
|
10
|
+
|
11
|
+
# Wraps a raw value in a {Data::Right}
|
12
|
+
#
|
13
|
+
# @param value [Any] the value to wrap
|
14
|
+
# @return [Data::Right]
|
15
|
+
def self.of(value)
|
16
|
+
Right.new(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
abstract_method :fmap
|
20
|
+
abstract_method :>>
|
21
|
+
|
22
|
+
abstract_method :either
|
23
|
+
abstract_method :and_then
|
24
|
+
abstract_method :or_else
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def of(value)
|
29
|
+
self.class.of(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Left < Either
|
34
|
+
# A Noop
|
35
|
+
#
|
36
|
+
# @return [Data::Left]
|
37
|
+
def fmap(*)
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# A Noop
|
42
|
+
#
|
43
|
+
# @return [Data::Left]
|
44
|
+
def >>(*)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
alias :and_then :>>
|
48
|
+
|
49
|
+
# Chain another operation which can result in a {Data::Either}
|
50
|
+
#
|
51
|
+
# @param f[#call] the next operation
|
52
|
+
# @return [Data::Left, Data::Right]
|
53
|
+
def or_else(f = nil, &blk)
|
54
|
+
(f || blk)[value]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Call a function on the value in the {Data::Left}
|
58
|
+
#
|
59
|
+
# @param f [#call] a function capable of processing the value
|
60
|
+
# @param _ [#call] a function that will never be called
|
61
|
+
def either(f, _)
|
62
|
+
f[value]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Right < Either
|
67
|
+
# Apply a function to a value contained in a {Data::Right}
|
68
|
+
#
|
69
|
+
# @param f [#call] the function to apply
|
70
|
+
# @return [Data::Right] the result of applying the function
|
71
|
+
# wrapped in a {Data::Right}
|
72
|
+
def fmap(f = nil, &blk)
|
73
|
+
self >> Composition.compose(method(:of), (f || blk))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Chain another operation which can result in a {Data::Either}
|
77
|
+
#
|
78
|
+
# @param f[#call] the next operation
|
79
|
+
# @return [Data::Left, Data::Right]
|
80
|
+
def >>(f = nil, &blk)
|
81
|
+
(f || blk)[value]
|
82
|
+
end
|
83
|
+
alias :and_then :>>
|
84
|
+
|
85
|
+
# A Noop
|
86
|
+
#
|
87
|
+
# @return [Data::Right]
|
88
|
+
def or_else(*)
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
# Call a function on the value in the {Data::Right}
|
93
|
+
#
|
94
|
+
# @param _ [#call] a function that will never be called
|
95
|
+
# @param f [#call] a function capable of processing the value
|
96
|
+
def either(_, f)
|
97
|
+
f[value]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "abstract_type"
|
2
|
+
require "equalizer"
|
3
|
+
|
4
|
+
module Unsound
|
5
|
+
module Data
|
6
|
+
# @abstract
|
7
|
+
class Maybe
|
8
|
+
include AbstractType
|
9
|
+
|
10
|
+
# Wraps a raw value in a {Data::Just}
|
11
|
+
#
|
12
|
+
# @param value [Any] the value to wrap
|
13
|
+
# @return [Data::Just]
|
14
|
+
def self.of(value)
|
15
|
+
Just.new(value)
|
16
|
+
end
|
17
|
+
|
18
|
+
abstract_method :fmap
|
19
|
+
abstract_method :>>
|
20
|
+
|
21
|
+
abstract_method :and_then
|
22
|
+
abstract_method :or_else
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def of(value)
|
27
|
+
self.class.of(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Nothing < Maybe
|
32
|
+
include Equalizer.new
|
33
|
+
|
34
|
+
# A Noop
|
35
|
+
#
|
36
|
+
# @return [Data::Nothing]
|
37
|
+
def fmap(*)
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# A Noop
|
42
|
+
#
|
43
|
+
# @return [Data::Nothing]
|
44
|
+
def >>(*)
|
45
|
+
self
|
46
|
+
end
|
47
|
+
alias :and_then :>>
|
48
|
+
|
49
|
+
# Chain another operation which can result in a {Data::Maybe}
|
50
|
+
#
|
51
|
+
# @param f[#call] the next operation
|
52
|
+
# @return [Data::Nothing, Data::Just]
|
53
|
+
def or_else(f = nil, &blk)
|
54
|
+
(f || blk).call
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Just < Maybe
|
59
|
+
include Concord::Public.new(:value)
|
60
|
+
|
61
|
+
# Apply a function to a value contained in a {Data::Just}
|
62
|
+
#
|
63
|
+
# @param f [#call] the function to apply
|
64
|
+
# @return [Data::Just] the result of applying the function
|
65
|
+
# wrapped in a {Data::Just}
|
66
|
+
def fmap(f = nil, &blk)
|
67
|
+
self >> Composition.compose(method(:of), (f || blk))
|
68
|
+
end
|
69
|
+
|
70
|
+
# Chain another operation which can result in a {Data::Maybe}
|
71
|
+
#
|
72
|
+
# @param f[#call] the next operation
|
73
|
+
# @return [Data::Nothing, Data::Just]
|
74
|
+
def >>(f = nil, &blk)
|
75
|
+
(f || blk)[value]
|
76
|
+
end
|
77
|
+
alias :and_then :>>
|
78
|
+
|
79
|
+
# A Noop
|
80
|
+
#
|
81
|
+
# @return [Data::Just]
|
82
|
+
def or_else(*)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/unsound/version.rb
CHANGED
@@ -0,0 +1,92 @@
|
|
1
|
+
require "shared/functor_examples"
|
2
|
+
require "shared/monad_examples"
|
3
|
+
|
4
|
+
require "unsound"
|
5
|
+
|
6
|
+
RSpec.describe Unsound::Data::Maybe do
|
7
|
+
let(:value) { double(:value) }
|
8
|
+
|
9
|
+
let(:instances) do
|
10
|
+
[
|
11
|
+
Unsound::Data::Nothing.new,
|
12
|
+
Unsound::Data::Just.new(value)
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has an abstract base class" do
|
17
|
+
expect {
|
18
|
+
Unsound::Data::Maybe.new
|
19
|
+
}.to raise_error(NotImplementedError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it_behaves_like "a Functor"
|
23
|
+
|
24
|
+
it_behaves_like "a Monad" do
|
25
|
+
let(:type) { Unsound::Data::Maybe }
|
26
|
+
|
27
|
+
specify { expect(type.of(value)).to eq(Unsound::Data::Just.new(value)) }
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#and_then" do
|
31
|
+
let(:and_then) do
|
32
|
+
->(value) { Unsound::Data::Just.new([value, value]) }
|
33
|
+
end
|
34
|
+
|
35
|
+
context "a just" do
|
36
|
+
let(:just) { Unsound::Data::Just.new(value) }
|
37
|
+
let(:value) { double(:value) }
|
38
|
+
|
39
|
+
context "a function" do
|
40
|
+
it "applies the function over the value" do
|
41
|
+
expect(just.and_then(and_then)).to eq(and_then.call(value))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "a block" do
|
46
|
+
it "applies the block over the value" do
|
47
|
+
expect(just.and_then(&and_then)).to eq(and_then.call(value))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "a nothing" do
|
53
|
+
let(:nothing) { Unsound::Data::Nothing.new }
|
54
|
+
let(:error) { double(:error) }
|
55
|
+
|
56
|
+
it "is a noop returning self" do
|
57
|
+
expect(nothing.and_then(and_then)).to eq(nothing)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#or_else" do
|
63
|
+
let(:or_else) do
|
64
|
+
-> { Unsound::Data::Nothing.new }
|
65
|
+
end
|
66
|
+
|
67
|
+
context "a just" do
|
68
|
+
let(:just) { Unsound::Data::Right.new(value) }
|
69
|
+
let(:value) { double(:value) }
|
70
|
+
|
71
|
+
it "is a noop returning self" do
|
72
|
+
expect(just.or_else(or_else)).to eq(just)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "a nothing" do
|
77
|
+
let(:nothing) { Unsound::Data::Nothing.new }
|
78
|
+
|
79
|
+
context "a function" do
|
80
|
+
it "calls the function" do
|
81
|
+
expect(nothing.or_else(or_else)).to eq(or_else.call)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "a block" do
|
86
|
+
it "yields to the block" do
|
87
|
+
expect(nothing.or_else(&or_else)).to eq(or_else.call)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unsound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Swan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concord
|
@@ -86,6 +86,8 @@ files:
|
|
86
86
|
- lib/unsound/composition.rb
|
87
87
|
- lib/unsound/control.rb
|
88
88
|
- lib/unsound/data.rb
|
89
|
+
- lib/unsound/data/either.rb
|
90
|
+
- lib/unsound/data/maybe.rb
|
89
91
|
- lib/unsound/version.rb
|
90
92
|
- spec/integration/try_spec.rb
|
91
93
|
- spec/shared/functor_examples.rb
|
@@ -93,6 +95,7 @@ files:
|
|
93
95
|
- spec/spec_helper.rb
|
94
96
|
- spec/unit/unsound/control_spec.rb
|
95
97
|
- spec/unit/unsound/data/either_spec.rb
|
98
|
+
- spec/unit/unsound/data/maybe_spec.rb
|
96
99
|
- unsound.gemspec
|
97
100
|
homepage: https://github.com/pdswan/unsound
|
98
101
|
licenses:
|
@@ -125,3 +128,4 @@ test_files:
|
|
125
128
|
- spec/spec_helper.rb
|
126
129
|
- spec/unit/unsound/control_spec.rb
|
127
130
|
- spec/unit/unsound/data/either_spec.rb
|
131
|
+
- spec/unit/unsound/data/maybe_spec.rb
|