mon 0.0.2

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.
@@ -0,0 +1,190 @@
1
+
2
+ module Mon
3
+
4
+ module Monad
5
+
6
+ require_relative 'chainable_monad'
7
+ require_relative 'monad'
8
+
9
+ class Try < Monad
10
+ include ChainableMonad
11
+
12
+ def self.[](obj)
13
+ if obj.is_a? Failure
14
+ Failure[obj.get]
15
+ elsif obj
16
+ Success[obj]
17
+ else
18
+ Failure[false]
19
+ end
20
+ end
21
+
22
+ def self::to *obj, &f
23
+ begin
24
+ if obj.length == 1 and obj[0].is_a? Proc
25
+ raise RuntimeError("Provided too many procs!") if f
26
+ Success[obj.call]
27
+ elsif f and obj.length > 0
28
+ Success[f.call(*obj)]
29
+ else
30
+ raise RuntimeError("Failed to provide block") if not f
31
+ Success[f.call]
32
+ end
33
+ rescue StandardError => e
34
+ Failure[e]
35
+ end
36
+ end
37
+
38
+ def self::valid?(o)
39
+ o.is_a? self.class
40
+ end
41
+
42
+ class << self
43
+ protected :new
44
+ end
45
+ end
46
+
47
+ class Success < Try
48
+
49
+ def initialize(obj)
50
+ if obj.is_a? Success
51
+ @obj = obj.get
52
+ else
53
+ @obj = obj
54
+ end
55
+ super()
56
+ end
57
+
58
+ def self.[](obj)
59
+ if (obj.is_a? Failure) or (obj.is_a? Success)
60
+ obj
61
+ else
62
+ Success.new(obj)
63
+ end
64
+ end
65
+
66
+ def bind &fun
67
+ begin
68
+ Success[fun.call(@obj)]
69
+ rescue StandardError => e
70
+ Failure[e]
71
+ end
72
+ end
73
+
74
+ def _canBind?(name)
75
+ @obj.respond_to? name
76
+ end
77
+
78
+ def or obj, &f
79
+ @obj
80
+ end
81
+
82
+ def orFail
83
+ @obj
84
+ end
85
+
86
+ def unwrap
87
+ @obj
88
+ end
89
+
90
+ def eql?(other)
91
+ if other.is_a? Success
92
+ @obj == other.unwrap
93
+ else
94
+ @obj == other
95
+ end
96
+ end
97
+
98
+ def ==(o)
99
+ eql?(o)
100
+ end
101
+
102
+ def equal?(o)
103
+ eql?(o)
104
+ end
105
+
106
+ def to_s
107
+ "Success[#{@obj}]"
108
+ end
109
+
110
+ def self::valid?(o)
111
+ o.is_a? self.class
112
+ end
113
+
114
+ class << self
115
+ protected :new
116
+ end
117
+
118
+ protected :unwrap, :_canBind?
119
+ end
120
+
121
+ class Failure < Try
122
+
123
+ def initialize(err)
124
+ @err = err
125
+ end
126
+
127
+ def self.[](err)
128
+ self.new(err)
129
+ end
130
+
131
+ def bind &fun
132
+ self
133
+ end
134
+
135
+ def or obj, &f
136
+ if f and obj
137
+ f.call(obj)
138
+ elsif f
139
+ f.call
140
+ else
141
+ obj
142
+ end
143
+ end
144
+
145
+ def orFail &f
146
+ if f
147
+ f.call(@err)
148
+ elsif @err.is_a? Exception
149
+ raise @err
150
+ else
151
+ @err
152
+ end
153
+ end
154
+
155
+ def unwrap
156
+ @err
157
+ end
158
+
159
+ def _canBind? name
160
+ true
161
+ end
162
+
163
+ def to_s
164
+ "Failure[#{@err}]"
165
+ end
166
+
167
+ def eql?(other)
168
+ (other.is_a? Failure) ? (@err == other.unwrap) : false
169
+ end
170
+
171
+ def ==(o)
172
+ eql?(o)
173
+ end
174
+
175
+ def equal?(o)
176
+ eql?(o)
177
+ end
178
+
179
+ def self::valid?(o)
180
+ o.is_a? self.class
181
+ end
182
+
183
+ class << self
184
+ protected :new
185
+ end
186
+
187
+ protected :unwrap, :_canBind?
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,310 @@
1
+
2
+ require 'contracts'
3
+ require_relative '../lib/mon'
4
+
5
+ include Contracts
6
+
7
+ Contract Mon::C::List[Num] => Mon::C::List[Num]
8
+ def listSquare(i)
9
+ i.bind { |i| i * i }
10
+ end
11
+
12
+ Contract Mon::C::Maybe[Num] => Mon::C::Maybe[Num]
13
+ def maybeSquare(i)
14
+ i.bind { |i| i * i }
15
+ end
16
+
17
+ Contract Mon::C::Try[Num] => Mon::C::Try[Num]
18
+ def trySquare(i)
19
+ i.bind { |i| i * i }
20
+ end
21
+
22
+ Contract Mon::C::Lazy[Num] => Mon::C::Lazy[Num]
23
+ def lazySquare(i)
24
+ i.bind { |i| i * i }
25
+ end
26
+
27
+ Contract Mon::C::Future[Num] => Mon::C::Future[Num]
28
+ def futureSquare(i)
29
+ i.bind { |i| i * i }
30
+ end
31
+
32
+ Contract Mon::C::React[Num] => Mon::C::React[Num]
33
+ def reactSquare(i)
34
+ i.bind { |i| i * i }
35
+ end
36
+
37
+ Contract Mon::C::Monad[Num] => Mon::C::Monad[Num]
38
+ def monadSquare(i)
39
+ i.bind { |i| i * i }
40
+ end
41
+
42
+ Contract Mon::C::Try[Mon::C::Maybe[Num]] => Mon::C::Try[Mon::C::Maybe[Num]]
43
+ def tryMaybeSquare(i)
44
+ i.bind { |j| j.bind { |k| k * k } }
45
+ end
46
+
47
+ Contract Mon::C::Try[Or[Num, String]] => Mon::C::Maybe[String]
48
+ def tryStringify(i)
49
+ Mon::M::Maybe[i.bind { |j| "We got: #{ j.to_s }" }._]
50
+ end
51
+
52
+ module Mon
53
+
54
+ module Contract
55
+
56
+ describe "List" do
57
+
58
+ it "should accept Lists" do
59
+ listSquare(Mon::M::List[1, 2, 3]).should eql Mon::M::List[1, 4, 9]
60
+ end
61
+
62
+ it "should accept empty Lists" do
63
+ listSquare(Mon::M::List[]).should eql Mon::M::List[]
64
+ end
65
+
66
+ it "should be internal-type aware" do
67
+ expect {
68
+ listSquare(Mon::M::List["test"])
69
+ }.to raise_error(ContractError)
70
+ end
71
+
72
+ it "shouldn't accept any other types" do
73
+ expect {
74
+ listSquare([5])
75
+ }.to raise_error(ContractError)
76
+ end
77
+
78
+ end
79
+
80
+ describe "Maybe" do
81
+
82
+ it "should accept Maybe monads" do
83
+ maybeSquare(Mon::M::Maybe[5]).should eql Mon::M::Maybe[25]
84
+ end
85
+
86
+ it "should accept empty Maybe monads" do
87
+ maybeSquare(Mon::M::None[]).should eql Mon::M::None[]
88
+ end
89
+
90
+ it "shouldn't accept Maybe monads containing incorrect types" do
91
+ expect {
92
+ maybeSquare(Mon::M::Maybe["test"])
93
+ }.to raise_error(ContractError)
94
+ end
95
+
96
+ it "shouldn't accept any other types" do
97
+ expect {
98
+ maybeSquare(5)
99
+ }.to raise_error(ContractError)
100
+ end
101
+
102
+ end
103
+
104
+ describe "Try" do
105
+
106
+ it "should accept Try monads" do
107
+ trySquare(Mon::M::Try[5]).should eql Mon::M::Success[25]
108
+ end
109
+
110
+ it "should accept Failures as well as Successes" do
111
+ trySquare(Mon::M::Try.to { raise RuntimeError.new }).should be_a Mon::M::Failure
112
+ end
113
+
114
+ it "shouldn't accept Successes of the incorrect type" do
115
+ expect {
116
+ trySquare(Mon::M::Try["test"])
117
+ }.to raise_error(ContractError)
118
+ end
119
+
120
+ it "shouldn't accept non-Try types" do
121
+ expect {
122
+ trySquare(5)
123
+ }.to raise_error(ContractError)
124
+ end
125
+
126
+ it "shouldn't accept non-Try monadic types" do
127
+ expect {
128
+ trySquare(Mon::M::Maybe[5])
129
+ }.to raise_error(ContractError)
130
+ end
131
+ end
132
+
133
+ describe "Lazy" do
134
+
135
+ it "should accept Lazy monads" do
136
+ lazySquare(Mon::M::Lazy[5]).should be_a Mon::M::Pending
137
+ end
138
+
139
+ it "should accept Pending as well as Final" do
140
+ lazySquare(Mon::M::Lazy.eventually { sleep 1; 5 }).should be_a Mon::M::Lazy
141
+ end
142
+
143
+ it "shouldn't accept Finals of the incorrect type" do
144
+ expect {
145
+ lazySquare(Mon::M::Try["test"])
146
+ }.to raise_error(ContractError)
147
+ end
148
+
149
+ it "shouldn't accept non-Lazy types" do
150
+ expect {
151
+ lazySquare(5)
152
+ }.to raise_error(ContractError)
153
+ end
154
+
155
+ it "shouldn't accept non-Lazy monadic types" do
156
+ expect {
157
+ lazySquare(Mon::M::Try[5])
158
+ }.to raise_error(ContractError)
159
+ end
160
+ end
161
+
162
+ describe "Future" do
163
+
164
+ it "should accept Future monads" do
165
+ futureSquare(Mon::M::Future.eventually { 5 }).should eql Mon::M::Future[25]
166
+ end
167
+
168
+ it "should accept Promise as well as Complete" do
169
+ futureSquare(Mon::M::Future.eventually { sleep 1; 5 }).should eql Mon::M::Future[25]
170
+ end
171
+
172
+ it "shouldn't accept Completes of the incorrect type" do
173
+ expect {
174
+ futureSquare(Mon::M::FutureComplete["test"])
175
+ }.to raise_error(ContractError)
176
+ end
177
+
178
+ it "shouldn't accept non-Future types" do
179
+ expect {
180
+ futureSquare(5)
181
+ }.to raise_error(ContractError)
182
+ end
183
+
184
+ it "shouldn't accept non-Future monadic types" do
185
+ expect {
186
+ futureSquare(Mon::M::Try[5])
187
+ }.to raise_error(ContractError)
188
+ end
189
+ end
190
+
191
+ describe "React" do
192
+
193
+ it "should accept Reactron monads" do
194
+ reactSquare(Mon::M::React[5]).should eql Mon::M::React[25]
195
+ end
196
+
197
+ it "should accept Reactors as well as Reactrons" do
198
+ reactSquare(Mon::M::React[1].bind { |i| i * 5 }).should eql Mon::M::React[25]
199
+ end
200
+
201
+ it "shouldn't accept Reactrons of the incorrect type" do
202
+ expect {
203
+ reactSquare(Mon::M::React["test"])
204
+ }.to raise_error(ContractError)
205
+ end
206
+
207
+ it "shouldn't accept Reactors of the incorrect type" do
208
+ expect {
209
+ reactSquare(Mon::M::React["tset"].bind { |s| s.reverse })
210
+ }.to raise_error(ContractError)
211
+ end
212
+
213
+ it "shouldn't accept non-React types" do
214
+ expect {
215
+ reactSquare(5)
216
+ }.to raise_error(ContractError)
217
+ end
218
+
219
+ it "shouldn't accept non-React monadic types" do
220
+ expect {
221
+ reactSquare(Mon::M::Try[5])
222
+ }.to raise_error(ContractError)
223
+ end
224
+ end
225
+
226
+ describe "Monad" do
227
+
228
+ it "should accept List" do
229
+ monadSquare(Mon::M::List[4, 5]).should eql Mon::M::List[16, 25]
230
+ end
231
+
232
+ it "should accept Maybe" do
233
+ monadSquare(Mon::M::Maybe[5]).should eql Mon::M::Maybe[25]
234
+ end
235
+
236
+ it "should accept None" do
237
+ monadSquare(Mon::M::None[]).should eql Mon::M::None[]
238
+ end
239
+
240
+ it "should accept Try" do
241
+ monadSquare(Mon::M::Try.to { 5 }).should eql Mon::M::Success[25]
242
+ end
243
+
244
+ it "should accept Failure" do
245
+ monadSquare(Mon::M::Try.to { throw RuntimeError.new }).should be_a Mon::M::Failure
246
+ end
247
+
248
+ it "should accept Lazy" do
249
+ monadSquare(Mon::M::Lazy.eventually { 5 }).should be_a Mon::M::Pending
250
+ end
251
+
252
+ it "should accept Final" do
253
+ monadSquare(Mon::M::Lazy[5]).should eql Mon::M::Final[25]
254
+ end
255
+
256
+ it "should accept Future" do
257
+ monadSquare(Mon::M::Future.eventually { sleep 1; 5 }).should eql Mon::M::Future[25]
258
+ end
259
+
260
+ it "should accept FutureComplete" do
261
+ monadSquare(Mon::M::Future[5]).should eql Mon::M::Future[25]
262
+ end
263
+
264
+ it "should accept React" do
265
+ monadSquare(Mon::M::React[5]).should eql Mon::M::React[25]
266
+ end
267
+
268
+ it "should accept Reactor" do
269
+ monadSquare(Mon::M::React[1].bind { |i| i * 5 }).should eql Mon::M::React[25]
270
+ end
271
+ end
272
+
273
+ describe "Nesting" do
274
+
275
+ it "should nest correctly" do
276
+ tryMaybeSquare(Mon::M::Try[Mon::M::Maybe[5]]).should eql Mon::M::Try[Mon::M::Maybe[25]]
277
+ end
278
+
279
+ it "should nest correctly with other types" do
280
+ tryStringify(Mon::M::Try[5]).should eql Mon::M::Maybe["We got: 5"]
281
+ tryStringify(Mon::M::Try["test"]).should eql Mon::M::Maybe["We got: test"]
282
+ end
283
+
284
+ it "should catch improper types in nested values (monad)" do
285
+ expect {
286
+ tryMaybeSquare(Mon::M::Try[Mon::M::Maybe["test"]])
287
+ }.to raise_error(ContractError)
288
+ end
289
+
290
+ it "should catch improper types in nested values (non-monad)" do
291
+ expect {
292
+ tryStringify(Mon::M::Try[:some_symbol])
293
+ }.to raise_error(ContractError)
294
+ end
295
+
296
+ it "should catch massively wrong type errors 1" do
297
+ expect {
298
+ tryStringify("test")
299
+ }.to raise_error(ContractError)
300
+ end
301
+
302
+ it "should catch massively wrong type errors 2" do
303
+ expect {
304
+ tryMaybeSquare("test")
305
+ }.to raise_error(ContractError)
306
+ end
307
+ end
308
+ end
309
+
310
+ end