fr 0.9.1

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.
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