fr 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/README.md +0 -0
  2. data/Rakefile +81 -0
  3. data/lib/fr.rb +44 -0
  4. data/lib/fr/applicative.rb +0 -0
  5. data/lib/fr/array.rb +168 -0
  6. data/lib/fr/boolean.rb +15 -0
  7. data/lib/fr/either.rb +94 -0
  8. data/lib/fr/errors.rb +5 -0
  9. data/lib/fr/errors/zipper_error.rb +8 -0
  10. data/lib/fr/functor.rb +11 -0
  11. data/lib/fr/functor/array.rb +15 -0
  12. data/lib/fr/functor/either.rb +17 -0
  13. data/lib/fr/functor/maybe.rb +17 -0
  14. data/lib/fr/maybe.rb +76 -0
  15. data/lib/fr/monad.rb +236 -0
  16. data/lib/fr/monad/array.rb +21 -0
  17. data/lib/fr/monad/either.rb +18 -0
  18. data/lib/fr/monad/maybe.rb +24 -0
  19. data/lib/fr/monad/random.rb +50 -0
  20. data/lib/fr/monad/reader.rb +45 -0
  21. data/lib/fr/monad/state.rb +62 -0
  22. data/lib/fr/monad/writer.rb +49 -0
  23. data/lib/fr/monoid.rb +52 -0
  24. data/lib/fr/monoid/array.rb +13 -0
  25. data/lib/fr/monoid/boolean.rb +23 -0
  26. data/lib/fr/monoid/either.rb +13 -0
  27. data/lib/fr/monoid/maybe.rb +15 -0
  28. data/lib/fr/monoid/numeric.rb +31 -0
  29. data/lib/fr/monoid/string.rb +11 -0
  30. data/lib/fr/numeric.rb +1 -0
  31. data/lib/fr/object.rb +45 -0
  32. data/lib/fr/string.rb +1 -0
  33. data/lib/fr/szipper.rb +36 -0
  34. data/lib/fr/szipper/abstract_cursor.rb +63 -0
  35. data/lib/fr/szipper/edited_cursor.rb +62 -0
  36. data/lib/fr/szipper/enumerable.rb +266 -0
  37. data/lib/fr/szipper/head_cursor.rb +71 -0
  38. data/lib/fr/szipper/head_prev_cursor.rb +112 -0
  39. data/lib/fr/szipper/memoized_cursor.rb +59 -0
  40. data/lib/fr/szipper/node.rb +67 -0
  41. data/lib/fr/szipper/tail_next_cursor.rb +68 -0
  42. data/lib/fr/thunk.rb +73 -0
  43. data/lib/fr/tzipper.rb +38 -0
  44. data/lib/fr/tzipper/abstract_cursor.rb +351 -0
  45. data/lib/fr/tzipper/dangling_cursor.rb +107 -0
  46. data/lib/fr/tzipper/edited_cursor.rb +161 -0
  47. data/lib/fr/tzipper/memoized_cursor.rb +129 -0
  48. data/lib/fr/tzipper/node.rb +57 -0
  49. data/lib/fr/tzipper/path.rb +125 -0
  50. data/lib/fr/tzipper/root_cursor.rb +124 -0
  51. data/lib/fr/unfold.rb +93 -0
  52. data/spec/examples/array.example +0 -0
  53. data/spec/examples/monads/array.example +0 -0
  54. data/spec/examples/monads/maybe.example +0 -0
  55. data/spec/examples/monads/random.example +0 -0
  56. data/spec/examples/monads/state.example +0 -0
  57. data/spec/examples/szipper.example +652 -0
  58. data/spec/examples/thunk.example +0 -0
  59. data/spec/examples/tzipper.example +0 -0
  60. data/spec/examples/unfold.example +90 -0
  61. data/spec/spec_helper.rb +23 -0
  62. data/spec/support/szipper/model.rb +225 -0
  63. data/spec/support/szipper/nodel.rb +144 -0
  64. 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