fr 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +0 -0
- data/Rakefile +81 -0
- data/lib/fr.rb +44 -0
- data/lib/fr/applicative.rb +0 -0
- data/lib/fr/array.rb +168 -0
- data/lib/fr/boolean.rb +15 -0
- data/lib/fr/either.rb +94 -0
- data/lib/fr/errors.rb +5 -0
- data/lib/fr/errors/zipper_error.rb +8 -0
- data/lib/fr/functor.rb +11 -0
- data/lib/fr/functor/array.rb +15 -0
- data/lib/fr/functor/either.rb +17 -0
- data/lib/fr/functor/maybe.rb +17 -0
- data/lib/fr/maybe.rb +76 -0
- data/lib/fr/monad.rb +236 -0
- data/lib/fr/monad/array.rb +21 -0
- data/lib/fr/monad/either.rb +18 -0
- data/lib/fr/monad/maybe.rb +24 -0
- data/lib/fr/monad/random.rb +50 -0
- data/lib/fr/monad/reader.rb +45 -0
- data/lib/fr/monad/state.rb +62 -0
- data/lib/fr/monad/writer.rb +49 -0
- data/lib/fr/monoid.rb +52 -0
- data/lib/fr/monoid/array.rb +13 -0
- data/lib/fr/monoid/boolean.rb +23 -0
- data/lib/fr/monoid/either.rb +13 -0
- data/lib/fr/monoid/maybe.rb +15 -0
- data/lib/fr/monoid/numeric.rb +31 -0
- data/lib/fr/monoid/string.rb +11 -0
- data/lib/fr/numeric.rb +1 -0
- data/lib/fr/object.rb +45 -0
- data/lib/fr/string.rb +1 -0
- data/lib/fr/szipper.rb +36 -0
- data/lib/fr/szipper/abstract_cursor.rb +63 -0
- data/lib/fr/szipper/edited_cursor.rb +62 -0
- data/lib/fr/szipper/enumerable.rb +266 -0
- data/lib/fr/szipper/head_cursor.rb +71 -0
- data/lib/fr/szipper/head_prev_cursor.rb +112 -0
- data/lib/fr/szipper/memoized_cursor.rb +59 -0
- data/lib/fr/szipper/node.rb +67 -0
- data/lib/fr/szipper/tail_next_cursor.rb +68 -0
- data/lib/fr/thunk.rb +73 -0
- data/lib/fr/tzipper.rb +38 -0
- data/lib/fr/tzipper/abstract_cursor.rb +351 -0
- data/lib/fr/tzipper/dangling_cursor.rb +107 -0
- data/lib/fr/tzipper/edited_cursor.rb +161 -0
- data/lib/fr/tzipper/memoized_cursor.rb +129 -0
- data/lib/fr/tzipper/node.rb +57 -0
- data/lib/fr/tzipper/path.rb +125 -0
- data/lib/fr/tzipper/root_cursor.rb +124 -0
- data/lib/fr/unfold.rb +93 -0
- data/spec/examples/array.example +0 -0
- data/spec/examples/monads/array.example +0 -0
- data/spec/examples/monads/maybe.example +0 -0
- data/spec/examples/monads/random.example +0 -0
- data/spec/examples/monads/state.example +0 -0
- data/spec/examples/szipper.example +652 -0
- data/spec/examples/thunk.example +0 -0
- data/spec/examples/tzipper.example +0 -0
- data/spec/examples/unfold.example +90 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/szipper/model.rb +225 -0
- data/spec/support/szipper/nodel.rb +144 -0
- metadata +116 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# Because Either is a monad, we automatically derive
|
3
|
+
# the implementation for `map` from `liftM`.
|
4
|
+
#
|
5
|
+
# module Fr
|
6
|
+
# class << Either
|
7
|
+
# include Functor
|
8
|
+
#
|
9
|
+
# def map(f = nil, x = nil, &block)
|
10
|
+
# (x.nil?) ?
|
11
|
+
# (block_given?) ?
|
12
|
+
# f.map(&block) : # f.map(fa){ .. }
|
13
|
+
# lambda{|x| x.map(&f) } :# f.map(lambda{ .. }).call(fa)
|
14
|
+
# x.map(&f) # f.map(lambda{ .. }, fa)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#
|
2
|
+
# Because Either is a monad, we automatically derive
|
3
|
+
# the implementation for `map` from `liftM`.
|
4
|
+
#
|
5
|
+
# module Fr
|
6
|
+
# class << Maybe
|
7
|
+
# include Functor
|
8
|
+
#
|
9
|
+
# def map(f = nil, x = nil, &block)
|
10
|
+
# (x.nil?) ?
|
11
|
+
# (block_given?) ?
|
12
|
+
# f.map(&block) : # f.map(fa){ .. }
|
13
|
+
# lambda{|x| x.map(&f) } :# f.map(lambda{ .. }).call(fa)
|
14
|
+
# x.map(&f) # f.map(lambda{ .. }, fa)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
data/lib/fr/maybe.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Fr
|
2
|
+
|
3
|
+
class Maybe
|
4
|
+
class Some < Maybe
|
5
|
+
def initialize(value)
|
6
|
+
@value = value
|
7
|
+
end
|
8
|
+
|
9
|
+
def map
|
10
|
+
Some.new(yield @value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def fold(default)
|
14
|
+
yield @value
|
15
|
+
end
|
16
|
+
|
17
|
+
def some?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def none?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
Maybe === other and other.fold(false){|x| x == @value }
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :eql?, :==
|
30
|
+
|
31
|
+
def hash
|
32
|
+
@value.hash
|
33
|
+
end
|
34
|
+
|
35
|
+
def inspect
|
36
|
+
"Fr.some(#{@value.inspect})"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class None_ < Maybe
|
41
|
+
def map
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def fold(default)
|
46
|
+
default
|
47
|
+
end
|
48
|
+
|
49
|
+
def some?
|
50
|
+
false
|
51
|
+
end
|
52
|
+
|
53
|
+
def none?
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
def ==(other)
|
58
|
+
None_ === other
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_method :eql?, :==
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
"Fr.none"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Singleton instance
|
69
|
+
None = None_.new
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
require "fr/functor/maybe"
|
75
|
+
require "fr/monoid/maybe"
|
76
|
+
require "fr/monad/maybe"
|
data/lib/fr/monad.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
module Fr
|
2
|
+
module Monad
|
3
|
+
# include Applicative
|
4
|
+
include Functor
|
5
|
+
|
6
|
+
# Perform each action, from left to right
|
7
|
+
# :: Monad m => [m a] -> m [a]
|
8
|
+
#
|
9
|
+
# @todo: Need trampoline for 2500+ ms
|
10
|
+
def sequence(ms)
|
11
|
+
ms.inject(unit []) do |prev, curr|
|
12
|
+
bind(prev) do |xs|
|
13
|
+
bind(curr) do |x|
|
14
|
+
unit(xs + [x])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Remove one level of monadic structure
|
21
|
+
# :: Monad m => m (m a) -> m a
|
22
|
+
#
|
23
|
+
# >> m.join(m.unit(m.unit(100)))
|
24
|
+
# => m.unit(100)
|
25
|
+
#
|
26
|
+
# >> Array.join([[1],[2],[3]])
|
27
|
+
# => [1,2,3]
|
28
|
+
#
|
29
|
+
def join(mm)
|
30
|
+
bind(mm){|x| x }
|
31
|
+
end
|
32
|
+
|
33
|
+
# :: Monad m => (a -> m b) -> [a] -> m [b]
|
34
|
+
#
|
35
|
+
# >> m.filter(lambda{|a| m.unit(a.even?) }).call([0,1,2,3])
|
36
|
+
# => m.unit([true,false,true,false])
|
37
|
+
#
|
38
|
+
def mapM(f, as = nil)
|
39
|
+
if as.nil?
|
40
|
+
lambda do |as|
|
41
|
+
sequence(as.map(&f))
|
42
|
+
end
|
43
|
+
else
|
44
|
+
sequence(as.map(&f))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# :: Monad m => [a] -> (a -> m b) -> m [b]
|
49
|
+
#
|
50
|
+
# >> m.forM([0,1,2,3]){|a| m.unit(a.even?) }
|
51
|
+
# => m.unit([true,false,true,false])
|
52
|
+
#
|
53
|
+
# >> m.forM([0,1,2,3], lambda{|a| m.unit(a.even?) })
|
54
|
+
# => m.unit([true,false,true,false])
|
55
|
+
#
|
56
|
+
# >> m.forM([0,1,2,3]).call(lambda{|a| m.unit(a.even?) })
|
57
|
+
# => m.unit([true,false,true,false])
|
58
|
+
#
|
59
|
+
def forM(as, f = nil, &block)
|
60
|
+
rest =
|
61
|
+
lambda do |f|
|
62
|
+
sequence(as.map(&block))
|
63
|
+
end
|
64
|
+
|
65
|
+
(f.nil?) ?
|
66
|
+
(block_given?) ?
|
67
|
+
rest.call(block) : rest : rest.call(f)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Left-to-right Kleisli composition of monads
|
71
|
+
# :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
|
72
|
+
#
|
73
|
+
# >> m.composeM(lambda{|a| m.unit(a.to_s) },
|
74
|
+
# lambda{|b| m.unit(b.length) }).call(450)
|
75
|
+
# => m.unit(3)
|
76
|
+
#
|
77
|
+
def composeM(f, g = nil, &block)
|
78
|
+
rest =
|
79
|
+
lambda do |g|
|
80
|
+
lambda do |a|
|
81
|
+
bind(f.call(a)) do |b|
|
82
|
+
g.call(b)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
(g.nil?) ?
|
88
|
+
(block_given?) ?
|
89
|
+
rest.call(block) : rest : rest.call(g)
|
90
|
+
end
|
91
|
+
|
92
|
+
# :: Monad m => (a -> m Boolean) -> [a] -> m [a]
|
93
|
+
#
|
94
|
+
# >> m.filterM(lambda{|a| m.unit(a.even?) }).call([0,1,2,3,4])
|
95
|
+
# => m.unit([0,2,4])
|
96
|
+
#
|
97
|
+
# Not implemented:
|
98
|
+
# >> [0,1,2,3,4].filterM{|a| m.unit(a.even?) }
|
99
|
+
# => m.unit([0,2,4])
|
100
|
+
#
|
101
|
+
def filterM(f, as = nil)
|
102
|
+
rec =
|
103
|
+
lambda do |as|
|
104
|
+
if as.empty?
|
105
|
+
unit([])
|
106
|
+
else
|
107
|
+
a, *as = as
|
108
|
+
bind(f.call(a)) do |bool|
|
109
|
+
bind(rec.call(as)) do |rest|
|
110
|
+
unit(bool ?
|
111
|
+
a.cons(rest) : rest)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
(as.nil?) ?
|
118
|
+
rec : rec.call(as)
|
119
|
+
end
|
120
|
+
|
121
|
+
# :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]
|
122
|
+
#
|
123
|
+
# >> m.zipM(lambda{|a,b| m.unit(a + b) }).call([1,2,3]).call([3,2,1])
|
124
|
+
# => m.unit([6,6,6])
|
125
|
+
#
|
126
|
+
# Not implemented:
|
127
|
+
# >> [1,2,3].zipM([3,2,1]){|a,b| m.unit(a + b) }
|
128
|
+
# => m.unit([6,6,6])
|
129
|
+
#
|
130
|
+
def zipM(f, xs = nil, ys = nil)
|
131
|
+
rest =
|
132
|
+
lambda do |xs|
|
133
|
+
lambda do |ys|
|
134
|
+
sequence(xs.zip(ys).map{|(x,y)| f.call(x, y) })
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
(ys.nil?) ?
|
139
|
+
(xs.nil?) ?
|
140
|
+
rest : rest.call(xs) : rest.call(xs).call(ys)
|
141
|
+
end
|
142
|
+
|
143
|
+
# :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
|
144
|
+
#
|
145
|
+
# >> m.foldM(lambda{|a,b| m.unit(a | b) }).call(0).call([1,2,4])
|
146
|
+
# => m.unit(7)
|
147
|
+
#
|
148
|
+
# Not implemented:
|
149
|
+
# >> [1,2,4].foldM(0){|a,b| m.unit(a | b) }
|
150
|
+
# => m.unit(7)
|
151
|
+
#
|
152
|
+
def foldM(f, seed = nil, bs = nil)
|
153
|
+
rest =
|
154
|
+
lambda do |seed|
|
155
|
+
lambda do |bs|
|
156
|
+
if bs.empty?
|
157
|
+
unit(seed)
|
158
|
+
else
|
159
|
+
b, *bs = bs
|
160
|
+
bind(f.call(seed, b)) do |seed_|
|
161
|
+
rec.call(seed_).call(bs)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
(bs.nil?) ?
|
168
|
+
(seed.nil?) ?
|
169
|
+
rest : rest.call(seed) : rest.call(seed).call(bs)
|
170
|
+
end
|
171
|
+
|
172
|
+
# :: Monad m => Bool -> m () -> m ()
|
173
|
+
def whenM(condition)
|
174
|
+
# todo
|
175
|
+
end
|
176
|
+
|
177
|
+
# :: Monad m => Bool -> m () -> m ()
|
178
|
+
def unlessM(condition)
|
179
|
+
# todo
|
180
|
+
end
|
181
|
+
|
182
|
+
# :: Monad m => (a -> b) -> m a -> m b
|
183
|
+
#
|
184
|
+
# >> m.liftM(lambda{|x| x.even? }).call(m.unit(30))
|
185
|
+
# => m.unit(true)
|
186
|
+
#
|
187
|
+
def liftM(f = nil, ma = nil, &block)
|
188
|
+
rest =
|
189
|
+
lambda do |f|
|
190
|
+
lambda do |ma|
|
191
|
+
bind(ma) do |a|
|
192
|
+
unit(f.call(a))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
(ma.nil?) ?
|
198
|
+
(block_given?) ?
|
199
|
+
rest.call(block).call(f) :# m.liftM(ma){ .. }
|
200
|
+
rest.call(f) :# m.liftM(lambda{ .. }).call(ma)
|
201
|
+
rest.call(f).call(ma) # m.liftM(lambda{ .. }, ma)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Automatically derive an implementation of `Functor.map` from `liftM`.
|
205
|
+
#
|
206
|
+
# Notice
|
207
|
+
# liftM :: Monad m => (a -> b) -> m a -> m b
|
208
|
+
# map :: Functor f => (a -> b) -> f a -> f b
|
209
|
+
alias_method :map, :liftM
|
210
|
+
|
211
|
+
# :: Monad m => m (a -> b) -> m a -> m b
|
212
|
+
#
|
213
|
+
# >> mf = m.unit(lambda{|x| lambda{|y| x + y }})
|
214
|
+
#
|
215
|
+
# >> ab = m.applyM(mf)
|
216
|
+
# >> ma = m.applyM(ab.call(m.unit 3))
|
217
|
+
# >> mb = ma.call(m.unit 4)
|
218
|
+
#
|
219
|
+
# => m.unit(7)
|
220
|
+
#
|
221
|
+
def applyM(mf, ma = nil)
|
222
|
+
rest =
|
223
|
+
lambda do |ma|
|
224
|
+
bind(mf) do |f|
|
225
|
+
bind(ma) do |a|
|
226
|
+
unit(f.call(a))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
(ma.nil?) ?
|
232
|
+
rest : rest.call(ma)
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
236
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class << Array
|
2
|
+
include Fr::Monad
|
3
|
+
|
4
|
+
# Evaluators
|
5
|
+
#############################################
|
6
|
+
|
7
|
+
def run(computation)
|
8
|
+
computation
|
9
|
+
end
|
10
|
+
|
11
|
+
# Combinators
|
12
|
+
#############################################
|
13
|
+
|
14
|
+
def unit(value)
|
15
|
+
[value]
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind(x, &f)
|
19
|
+
x.map(&f).inject([], &:concat)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fr
|
2
|
+
|
3
|
+
class << Either
|
4
|
+
include Monad
|
5
|
+
|
6
|
+
# Combinators
|
7
|
+
#############################################
|
8
|
+
|
9
|
+
def unit(value)
|
10
|
+
Either::Right.new(value)
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind(x, &f)
|
14
|
+
x.fold(lambda{|l| x }, lambda{|r| f.call(r) })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Fr
|
2
|
+
|
3
|
+
class << Maybe
|
4
|
+
include Monad
|
5
|
+
|
6
|
+
# Evaluators
|
7
|
+
#############################################
|
8
|
+
def run(computation)
|
9
|
+
computation
|
10
|
+
end
|
11
|
+
|
12
|
+
# Combinators
|
13
|
+
#############################################
|
14
|
+
|
15
|
+
def unit(value)
|
16
|
+
Maybe::Some.new(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
def bind(x, &f)
|
20
|
+
x.fold(x, &f)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Fr
|
2
|
+
|
3
|
+
class << Random
|
4
|
+
include Monad
|
5
|
+
|
6
|
+
# Evaluators
|
7
|
+
#############################################
|
8
|
+
|
9
|
+
def run(computation, scale = 1.0)
|
10
|
+
computation.call(scale)
|
11
|
+
end
|
12
|
+
|
13
|
+
def eval(computation, scale = 1.0)
|
14
|
+
computation.call(scale)[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Combinators
|
18
|
+
#############################################
|
19
|
+
|
20
|
+
def unit(value)
|
21
|
+
lambda do |scale|
|
22
|
+
[value, scale]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def bind(f, &g)
|
27
|
+
lambda do |scale|
|
28
|
+
value, scale = f.call(scale)
|
29
|
+
g.call(value).call(scale)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Actions
|
34
|
+
#############################################
|
35
|
+
|
36
|
+
def rand(limit = nil)
|
37
|
+
lambda do |scale|
|
38
|
+
[Kernel.rand(limit), scale]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def scale(number, zero)
|
43
|
+
# Linearly scale given number towards zero
|
44
|
+
lambda do |scale|
|
45
|
+
[zero + scale * (number - zero), scale]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|