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,62 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class EditedCursor < AbstractCursor
5
+ attr_reader :node
6
+
7
+ def initialize(node, prev)
8
+ @node, @prev =
9
+ node, prev
10
+ end
11
+
12
+ def head?
13
+ false
14
+ end
15
+
16
+ def tail?
17
+ @node.tail?
18
+ end
19
+
20
+ def prev(phantom = false)
21
+ if @prev.head?
22
+ HeadCursor.new(@prev.node.copy(next: @node))
23
+ else
24
+ EditedCursor.new(@prev.node.copy(next: @node), @prev.prev)
25
+ end
26
+ end
27
+
28
+ def append(node)
29
+ EditedCursor.new(node.copy(next: @node.next), self)
30
+ end
31
+
32
+ def prepend(node)
33
+ EditedCursor.new(node.copy(next: @node), prev)
34
+ end
35
+
36
+ def update(node = nil)
37
+ if node.nil?
38
+ EditedCursor.new(yield(@node).copy(next: @node.next), @prev)
39
+ else
40
+ EditedCursor.new(node.copy(next: @node.next), @prev)
41
+ end
42
+ end
43
+
44
+ def replace(node)
45
+ EditCursor.new(node, @prev)
46
+ end
47
+
48
+ def delete
49
+ if @prev.head?
50
+ HeadCursor.new(prev.node.copy(next: @node.next))
51
+ else
52
+ EditedCursor.new(prev.node.copy(next: @node.next), @prev.prev)
53
+ end
54
+ end
55
+
56
+ def inspect
57
+ "Cursor([..., #{@node.inspect}, ...])"
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,266 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ module Enumerable
5
+
6
+ def each
7
+ cursor = self
8
+ yield(cursor)
9
+
10
+ until cursor.tail?
11
+ cursor = cursor.next
12
+ yield(cursor)
13
+ end
14
+
15
+ self
16
+ end
17
+
18
+ def all?
19
+ catch(:done) do
20
+ each{|c| throw(:done, false) if !yield(c.node) }; true
21
+ end
22
+ end
23
+
24
+ def any?
25
+ catch(:done) do
26
+ each{|c| throw(:done, true) if yield(c.node) }; false
27
+ end
28
+ end
29
+
30
+ def chunk
31
+ # todo: defined?
32
+ end
33
+
34
+ def collect
35
+ unless tail?
36
+ current = yield(node)
37
+ next_ = Fr.thunk { self.next.collect{|n| yield(n) }.node }
38
+ replace(current.copy(next: next_))
39
+ else
40
+ update(yield(node))
41
+ end
42
+ end
43
+
44
+ def collect_concat
45
+ # todo: defined?
46
+ end
47
+
48
+ def count
49
+ foldl(0){|count, c| yield(c.node) ? count + 1 : count }
50
+ end
51
+
52
+ def cycle
53
+ # todo: defined?
54
+ end
55
+
56
+ def detect(default = nil)
57
+ catch(:done) do
58
+ each{|c| throw(:done, c) if yield(c.node) }; default
59
+ end
60
+ end
61
+
62
+ def drop(n)
63
+ # Seek n nodes forward
64
+ nth = n.times.inject(self.next(true)){|c,_| c.next(true) }
65
+ mth = node.copy(next: nth.prev.node.next)
66
+ replace(mth)
67
+ end
68
+
69
+ def drop_while
70
+ # todo
71
+ end
72
+
73
+ def entries
74
+ foldl([]){|entries, c| entries << c.node }
75
+ end
76
+
77
+ alias_method :find, :detect
78
+
79
+ def filter
80
+ node = filter_{|n| yield(n) }
81
+ unless node.nil?
82
+ replace(node)
83
+ else
84
+ prev(true).truncate
85
+ end
86
+ end
87
+
88
+ # @private
89
+ def filter_
90
+ unless tail?
91
+ Fr.thunk do
92
+ if yield(node)
93
+ node.copy(next: self.next.filter_{|n| yield(n) })
94
+ else
95
+ self.next.filter_{|n| yield(n) }
96
+ end
97
+ end
98
+ else
99
+ yield(node) ?
100
+ node : nil
101
+ end
102
+ end
103
+
104
+ alias_method :find_all, :filter
105
+
106
+ def index(default = nil)
107
+ position_ = position
108
+
109
+ catch(:done) do
110
+ each do |c|
111
+ if yield(c.node)
112
+ throw(:done, position_)
113
+ else
114
+ position_ += 1
115
+ end
116
+ end
117
+
118
+ default
119
+ end
120
+ end
121
+
122
+ alias_method :find_index, :index
123
+
124
+ # alias_method :first, :head
125
+ def first(n = 1)
126
+ if n == 1
127
+ head
128
+ else
129
+ # todo: defined?
130
+ end
131
+ end
132
+
133
+ def flat_map
134
+ # todo
135
+ end
136
+
137
+ def grep(pattern)
138
+ filter{|node| pattern === node }
139
+ end
140
+
141
+ def group_by
142
+ # todo: defined?
143
+ end
144
+
145
+ def include?(needle)
146
+ any?{|node| node == needle }
147
+ end
148
+
149
+ def foldl(svalue)
150
+ cursor = self
151
+ svalue = yield(svalue, cursor)
152
+ until cursor.tail?
153
+ cursor = cursor.next
154
+ svalue = yield(svalue, cursor)
155
+ end
156
+ svalue
157
+ end
158
+
159
+ alias_method :inject, :foldl
160
+
161
+ alias_method :map, :collect
162
+
163
+ def max
164
+ reduce{|a,b| a.node < b.node ? b : a }
165
+ end
166
+
167
+ def max_by
168
+ reduce{|a,b| yield(a.node) < yield(b.node) ? b : a }
169
+ end
170
+
171
+ def min
172
+ reduce{|a,b| a.node > b.node ? b : a }
173
+ end
174
+
175
+ def min_by
176
+ reduce{|a,b| yield(a.node) > yield(b.node) ? b : a }
177
+ end
178
+
179
+ def minmax
180
+ foldl([self, self]) do |(min,max), c|
181
+ [min.node > c.node ? c : min,
182
+ max.node < c.node ? c : max]
183
+ end
184
+ end
185
+
186
+ def minmax_by
187
+ foldl([self, self]) do |(min,max), c|
188
+ [yield(min.node) > yield(c.node) ? c : min,
189
+ yield(max.node) < yield(c.node) ? c : max]
190
+ end
191
+ end
192
+
193
+ def none?
194
+ catch(:done) do
195
+ each{|c| throw(:done, false) if yield(c.node) }; true
196
+ end
197
+ end
198
+
199
+ alias_method :one?, :any?
200
+
201
+ def partition
202
+ # todo
203
+ end
204
+
205
+ def reduce(op = nil)
206
+ # [].reduce(op) #=> nil
207
+ # [a].reduce(op) #=> a
208
+ # [a,b].reduce(op) #=> a op b
209
+ # [a,b,c].reduce(op) #=> a op b op c
210
+
211
+ svalue = self
212
+ cursor = self
213
+
214
+ unless op.nil?
215
+ until cursor.tail?
216
+ cursor = cursor.next
217
+ svalue = svalue.send(op, cursor)
218
+ end
219
+ else
220
+ until cursor.tail?
221
+ cursor = cursor.next
222
+ svalue = yield(svalue, cursor)
223
+ end
224
+ end
225
+
226
+ svalue
227
+ end
228
+
229
+ # todo:
230
+ # - api: this always returns a cursor pointed at tail (ugh)
231
+ # - api: should be lazy
232
+ # - bug: doesn't filter tail element
233
+ def reject
234
+ cursor = self
235
+ until cursor.tail?
236
+ cursor = yield(cursor.node) ?
237
+ cursor.replace(cursor.node.next) :
238
+ cursor.next
239
+ end
240
+ cursor
241
+ end
242
+
243
+ alias_method :select, :filter
244
+
245
+ def sort
246
+ # todo: defined?
247
+ end
248
+
249
+ def take(n)
250
+ # todo: defined?
251
+ end
252
+
253
+ def take_while
254
+ # todo: defined?
255
+ end
256
+
257
+ alias_method :to_a, :entries
258
+
259
+ def zip(stream, *streams)
260
+ # todo: should be lazy
261
+ end
262
+
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,71 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class HeadCursor < AbstractCursor
5
+ # @return [#next, #tail?]
6
+ attr_reader :node
7
+
8
+ def initialize(node)
9
+ @node = node
10
+ end
11
+
12
+ def position
13
+ 0
14
+ end
15
+
16
+ def head?
17
+ true
18
+ end
19
+
20
+ def tail?
21
+ @node.tail?
22
+ end
23
+
24
+ def head
25
+ self
26
+ end
27
+
28
+ def prev(phantom = false)
29
+ if phantom
30
+ HeadPrevCursor.new(@node)
31
+ else
32
+ raise Errors::ZipperError,
33
+ "head node has no prev"
34
+ end
35
+ end
36
+
37
+ def append(node)
38
+ EditedCursor.new(node.copy(next: @node.next), self)
39
+ end
40
+
41
+ def prepend(node)
42
+ HeadCursor.new(node.copy(next: @node))
43
+ end
44
+
45
+ def update(node = nil)
46
+ if node.nil?
47
+ HeadCursor.new(yield(@node).copy(next: @node.next))
48
+ else
49
+ HeadCursor.new(node.copy(next: @node.next))
50
+ end
51
+ end
52
+
53
+ def replace(node)
54
+ HeadCursor.new(node)
55
+ end
56
+
57
+ def delete
58
+ if tail?
59
+ HeadPrevCursor.new(nil)
60
+ else
61
+ HeadCursor.new(@node.next)
62
+ end
63
+ end
64
+
65
+ def inspect
66
+ "Cursor([#{@node.inspect}, ...])"
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,112 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class HeadPrevCursor < AbstractCursor
5
+ def initialize(node)
6
+ @node = node
7
+ end
8
+
9
+ def position
10
+ -1
11
+ end
12
+
13
+ def head?
14
+ @node.nil?
15
+ end
16
+
17
+ def tail?
18
+ @node.nil?
19
+ end
20
+
21
+ def empty?
22
+ @node.nil?
23
+ end
24
+
25
+ def head
26
+ if @node.nil?
27
+ raise Errors::ZipperError,
28
+ "head.prev has no head"
29
+ else
30
+ HeadCursor.new(@node)
31
+ end
32
+ end
33
+
34
+ def tail
35
+ if @node.nil?
36
+ raise Errors::ZipperError,
37
+ "head.prev has no tail"
38
+ else
39
+ HeadCursor.new(@node).tail
40
+ end
41
+ end
42
+
43
+ def prev(phantom = false)
44
+ if phantom
45
+ self
46
+ else
47
+ raise Errors::ZipperError,
48
+ "head.prev has no prev"
49
+ end
50
+ end
51
+
52
+ def next(phantom = false)
53
+ if tail?
54
+ if phantom
55
+ self
56
+ else
57
+ raise Errors::ZipperError,
58
+ "head.prev has no next"
59
+ end
60
+ else
61
+ HeadCursor.new(@node)
62
+ end
63
+ end
64
+
65
+ def append(node)
66
+ HeadCursor.new(node.copy(next: @node))
67
+ end
68
+
69
+ def prepend(node)
70
+ HeadCursor.new(node.copy(next: @node))
71
+ end
72
+
73
+ def update(node = nil)
74
+ if node.nil?
75
+ HeadCursor.new(yield(nil).copy(next: @node))
76
+ else
77
+ HeadCursor.new(node.copy(next: @node))
78
+ end
79
+ end
80
+
81
+ def replace(node)
82
+ HeadCursor.new(node)
83
+ end
84
+
85
+ def delete
86
+ if tail?
87
+ self
88
+ else
89
+ HeadCursor.new(@node)
90
+ end
91
+ end
92
+
93
+ def truncate
94
+ HeadPrevCursor.new(nil)
95
+ end
96
+
97
+ def inspect
98
+ if tail?
99
+ "Cursor([___])"
100
+ else
101
+ "Cursor([___, #{@node.inspect}, ...])"
102
+ end
103
+ end
104
+
105
+ def ==(other)
106
+ HeadPrevCursor === other and
107
+ not empty? ^ other.empty?
108
+ end
109
+ end
110
+
111
+ end
112
+ end