mon 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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