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,59 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class MemoizedCursor < AbstractCursor
5
+ # @return [#next, #tail?]
6
+ attr_reader :node
7
+
8
+ def initialize(node, prev)
9
+ @node, @prev =
10
+ node, prev
11
+ end
12
+
13
+ def prev(phantom = false)
14
+ @prev
15
+ end
16
+
17
+ def head?
18
+ false
19
+ end
20
+
21
+ def tail?
22
+ @node.tail?
23
+ end
24
+
25
+ def append(node)
26
+ EditedCursor.new(node.copy(next: @node.next), self)
27
+ end
28
+
29
+ def prepend(node)
30
+ EditedCursor.new(node.copy(next: @node), @prev)
31
+ end
32
+
33
+ def update(node = nil)
34
+ if node.nil?
35
+ EditedCursor.new(yield(@node).copy(next: @node.next), @prev)
36
+ else
37
+ EditedCursor.new(node.copy(next: @node.next), @prev)
38
+ end
39
+ end
40
+
41
+ def replace(node)
42
+ EditedCursor.new(node, @prev)
43
+ end
44
+
45
+ def delete
46
+ if @prev.head?
47
+ HeadCursor.new(@prev.node.copy(next: @node.next))
48
+ else
49
+ EditedCursor.new(@prev.node.copy(next: @node.next), @prev.prev)
50
+ end
51
+ end
52
+
53
+ def inspect
54
+ "Cursor([..., #{@node.inspect}, ...])"
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ # Example implementation for a stream node. For the
5
+ # purposes of traversal with a zipper, nodes can be
6
+ # anything that implements #copy, #next, and #tail?
7
+ #
8
+ # You'll probably want access to some datum too, like
9
+ # #value. Otherwise you'll be traversing a stream of
10
+ # otherwise useless cells.
11
+ #
12
+ class Node
13
+ attr_reader :value
14
+
15
+ attr_reader :next
16
+
17
+ def initialize(value, _next = nil)
18
+ @value, @next =
19
+ value, _next
20
+ end
21
+
22
+ def tail?
23
+ @next.nil?
24
+ end
25
+
26
+ def copy(options = {})
27
+ Node.new \
28
+ options.fetch(:value, @value),
29
+ options.fetch(:next, @next)
30
+ end
31
+
32
+ def inspect
33
+ "Node(#{@value})"
34
+ end
35
+ end
36
+
37
+ class << Node
38
+ def cons(value, _next)
39
+ Node.new(value, _next)
40
+ end
41
+
42
+ def tail(value)
43
+ Node.new(value)
44
+ end
45
+
46
+
47
+ # Convenient constructor to build a chain of Nodes
48
+ # from any "sequential" enum. That excludes things
49
+ # like Hash, Set, and Range because their traversal
50
+ # order isn't deterministic. Otherwise, anything that
51
+ # implements #reverse and #inject can be converted.
52
+ #
53
+ def build(enum)
54
+ if enum.empty?
55
+ raise Errors::ZipperError,
56
+ "enum is empty"
57
+ end
58
+
59
+ tail, *rest = enum.reverse
60
+ rest.inject(tail(tail)) do |_next, value|
61
+ cons(value, _next)
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,68 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class TailNextCursor < AbstractCursor
5
+ def initialize(prev)
6
+ @prev = prev
7
+ end
8
+
9
+ def empty?
10
+ true
11
+ end
12
+
13
+ def tail?
14
+ false
15
+ end
16
+
17
+ def head?
18
+ false
19
+ end
20
+
21
+ def tail
22
+ @prev
23
+ end
24
+
25
+ def prev(phantom = false)
26
+ @prev
27
+ end
28
+
29
+ def next(phantom = false)
30
+ if phantom
31
+ self
32
+ else
33
+ raise Errors::ZipperError,
34
+ "tail node has no next"
35
+ end
36
+ end
37
+
38
+ def append(node)
39
+ @prev.append(node)
40
+ end
41
+
42
+ def prepend(node)
43
+ @prev.append(node)
44
+ end
45
+
46
+ def update(node = nil)
47
+ if node.nil?
48
+ @prev.append(yield(nil).copy(next: nil))
49
+ else
50
+ @prev.append(node.copy(next: nil))
51
+ end
52
+ end
53
+
54
+ def replace(node)
55
+ EditedCursor.new(node, @prev)
56
+ end
57
+
58
+ def delete
59
+ @prev
60
+ end
61
+
62
+ def inspect
63
+ "Cursor([..., #{@prev.node.inspect}, ___])"
64
+ end
65
+ end
66
+
67
+ end
68
+ end
data/lib/fr/thunk.rb ADDED
@@ -0,0 +1,73 @@
1
+ module Fr
2
+
3
+ # Beware, there's no way to make a thunk value 100%
4
+ # compatible with the value it computes. For instance,
5
+ #
6
+ # String === "a" #=> true
7
+ # String === thunk { "a" } #=> false
8
+ #
9
+ # "a" == "a" #=> true
10
+ # "a" == thunk { "a" } #=> false
11
+ #
12
+ # x = "abc"
13
+ # y = thunk { x }
14
+ #
15
+ # x.equal?(x) #=> true
16
+ # y.equal?(y) #=> true
17
+ # x.equal?(y) #=> false
18
+ # y.equal?(x) #=> false
19
+ #
20
+ class Thunk < BasicObject
21
+ def initialize(block)
22
+ unless block.arity.zero?
23
+ raise ArgumentError,
24
+ "block must have zero arity"
25
+ end
26
+
27
+ @block = block
28
+ end
29
+
30
+ def thunk?
31
+ true
32
+ end
33
+
34
+ # Silence warnings about redefining __send__
35
+ verbose = $VERBOSE
36
+ $VERBOSE = nil
37
+
38
+ undef_method :!
39
+ undef_method :==
40
+ undef_method :!=
41
+ undef_method :instance_eval
42
+ undef_method :instance_exec
43
+
44
+ private
45
+
46
+ def __send__(name, *args, &block)
47
+ unless defined? @value
48
+ @value = @block.call
49
+ @block = nil
50
+ end
51
+
52
+ @value.__send__(name, *args, &block)
53
+ end
54
+
55
+ $VERBOSE = verbose
56
+
57
+ def method_missing(name, *args, &block)
58
+ unless defined? @value
59
+ @value = @block.call
60
+ @block = nil
61
+ end
62
+
63
+ @value.__send__(name, *args, &block)
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ class Object
70
+ def thunk?
71
+ false
72
+ end
73
+ end
data/lib/fr/tzipper.rb ADDED
@@ -0,0 +1,38 @@
1
+ module Fr
2
+ module TZipper
3
+ autoload :AbstractCursor, "fr/tzipper/abstract_cursor"
4
+ autoload :DanglingCursor, "fr/tzipper/dangling_cursor"
5
+ autoload :EditedCursor, "fr/tzipper/edited_cursor"
6
+ autoload :MemoizedCursor, "fr/tzipper/memoized_cursor"
7
+ autoload :RootCursor, "fr/tzipper/root_cursor"
8
+
9
+ autoload :AbstractPath, "fr/tzipper/path"
10
+ autoload :Hole, "fr/tzipper/path"
11
+ autoload :Root, "fr/tzipper/path"
12
+ autoload :Node, "fr/tzipper/node"
13
+ end
14
+
15
+ class << TZipper
16
+
17
+ # Constructors a zipper for traversing the given tree or
18
+ # subtree. The `node` value can be any structure where
19
+ #
20
+ # node#children
21
+ # an array of child nodes
22
+ #
23
+ # node#leaf?
24
+ # true if the node is not allowed have children
25
+ #
26
+ # node#copy(children: [...])
27
+ # creates a new node with the given children
28
+ #
29
+ # node#cons(xs)
30
+ # returns a new list [node, *xs]
31
+ #
32
+ # @return [AbstractCursor]
33
+ def build(node)
34
+ Fr::TZipper::RootCursor.new(node)
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,351 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class AbstractCursor
5
+
6
+ # @return [#leaf?, #children, #copy, #cons]
7
+ # abstract :node
8
+
9
+ # @return [AbstractPath]
10
+ # abstract :path
11
+
12
+ # @group Querying the Tree Location
13
+ #########################################################################
14
+
15
+ # (see AbstractPath#depth)
16
+ def depth
17
+ path.depth
18
+ end
19
+
20
+ # (see AbstractPath#first?)
21
+ def first?
22
+ path.first?
23
+ end
24
+
25
+ # (see AbstractPath#last?)
26
+ def last?
27
+ path.last?
28
+ end
29
+
30
+ # True if the node has no children
31
+ # abstract :leaf?
32
+
33
+ # True if the node has no parent
34
+ # abstract :root?
35
+
36
+ # Returns nodes between this zipper and the other, including `self.node`
37
+ # and `other.node` as end points. When this and the other zipper point to
38
+ # the same node within the tree, a single node is returned. Otherwise, the
39
+ # nodes are returned in order according to a left-to-right depth-first
40
+ # pre-order traversal.
41
+ #
42
+ # @note This method assumes `other` is a zipper for the same tree as the
43
+ # tree wrapped by `this`. In general, there is no way to know if that is
44
+ # or isn't the case, without comparing the entire tree. If this method
45
+ # is called on two different trees, the results are undefined.
46
+ #
47
+ # @return [Array]
48
+ def between(other)
49
+ # Collect ancestors of self, sorted oldest first (deepest last). This
50
+ # forms a boundary of nodes, which is called a "spine" below
51
+ zipper = self
52
+ lspine = [self]
53
+
54
+ until zipper.root?
55
+ zipper = zipper.up
56
+ lspine.unshift(zipper)
57
+ end
58
+
59
+ # Collect ancestors of self, sorted oldest first (deepest last). This
60
+ # forms a list of boundary nodes, which is called a "spine" below
61
+ zipper = other
62
+ rspine = [other]
63
+
64
+ until zipper.root?
65
+ zipper = zipper.up
66
+ rspine.unshift(zipper)
67
+ end
68
+
69
+ # Now we have two spines, both beginning with the root node. We remove
70
+ # the prefix common to both spines.
71
+ while a = lspine.first and b = rspine.first
72
+ if a.path == b.path
73
+ lspine.shift
74
+ rspine.shift
75
+ else
76
+ break
77
+ end
78
+ end
79
+
80
+ if lspine.empty?
81
+ # The other node is a child of self's node, and rspine contains all
82
+ # the nodes along the path between the two nodes, not including the
83
+ # self node.
84
+ return node.cons(rspine.map(&:node))
85
+
86
+ elsif rspine.empty?
87
+ # Self's node is a child of other's node, and lspine contains all
88
+ # the nodes along the path between the two nodes, not including the
89
+ # other node
90
+ return other.node.cons(lspine.map(&:node))
91
+
92
+ elsif lspine.head.path.position > rspine.head.path.position
93
+ # The first elements of lspine and rspine are siblings that share a
94
+ # common parent. Arrange them such that lspine is on the left, and
95
+ # so rspine is on the right
96
+ lspine, rspine = rspine, lspine
97
+ end
98
+
99
+ between = []
100
+
101
+ # Starting at the bottom of the left spine working upward, accumulate
102
+ # all the nodes to the right of the spine. Remember this is contained
103
+ # within the subtree under lspine.head
104
+ lspine.each{|z| between << z.node }
105
+ lspine.tail.reverse.each do |zipper|
106
+ until zipper.last?
107
+ zipper = zipper.next
108
+ between.concat(zipper.flatten)
109
+ end
110
+ end
111
+
112
+ # For the sibling nodes directly between (not including) lspine.head
113
+ # and rspine.head, we can accumulate the entire subtrees.
114
+ count = rspine.head.path.position - lspine.head.path.position - 1
115
+ zipper = lspine.head
116
+
117
+ count.times do
118
+ zipper = zipper.next
119
+ between.concat(zipper.flatten)
120
+ end
121
+
122
+ between << rspine.head.node
123
+
124
+ rspine.tail.each do |zipper|
125
+ count = zipper.path.position
126
+ zipper = zipper.first
127
+
128
+ # We have to do a bit more work to traverse the siblings in left-to-
129
+ # right order, because `zipper` is now the left spine. We start on
130
+ # the first sibling and move left a fixed number of times
131
+ count.times do
132
+ between.concat(zipper.flatten)
133
+ zipper = zipper.next
134
+ end
135
+
136
+ # Now zipper is along the left spine. We don't expand it here, but the
137
+ # next item in rspine is the next child along the left spine
138
+ between << zipper.node
139
+ end
140
+
141
+ between
142
+ end
143
+
144
+ # Flattens the subtree into an Array of nodes. The nodes are arranged
145
+ # according to a depth-first left-to-right preorder traversal.
146
+ #
147
+ # @return [Array]
148
+ def flatten
149
+ nodes = []
150
+ queue = [node]
151
+
152
+ while node = queue.shift
153
+ nodes << node
154
+ queue.unshift(*node.children) unless node.leaf?
155
+ end
156
+
157
+ nodes
158
+ end
159
+
160
+ # @group Traversing the Tree
161
+ #########################################################################
162
+
163
+ # Navigate to the first child node
164
+ #
165
+ # @return [MemoizedCursor]
166
+ def down
167
+ @__down ||= begin
168
+ if leaf?
169
+ raise Errors::ZipperError,
170
+ "cannot descend into leaf node"
171
+ end
172
+
173
+ head, *tail = @node.children
174
+
175
+ MemoizedCursor.new(head,
176
+ Hole.new([], @path, tail), self)
177
+ end
178
+ end
179
+
180
+ # Navigate to the first child node, or if there are no children,
181
+ # create a placeholder where the first child node will be placed
182
+ #
183
+ # @return [AbstractCursor]
184
+ def dangle
185
+ if node.leaf?
186
+ raise Errors::ZipperError,
187
+ "cannot descend into leaf node"
188
+ end
189
+
190
+ if leaf?
191
+ DanglingCursor.new(self)
192
+ else
193
+ down
194
+ end
195
+ end
196
+
197
+ # Navigate to the `nth` child node
198
+ #
199
+ # @return [AbstractCursor]
200
+ def child(n)
201
+ if n < 0
202
+ raise Errors::ZipperError,
203
+ "child index cannot be negative"
204
+ end
205
+
206
+ cursor = down
207
+ until n.zero?
208
+ cursor = cursor.next
209
+ n -= 1
210
+ end
211
+ cursor
212
+ end
213
+
214
+ # Returns a list of cursors, one pointing to each child node.
215
+ #
216
+ # @return [Array<MemoizedCursor>]
217
+ def children
218
+ children = []
219
+
220
+ unless leaf?
221
+ zipper = down
222
+ children << zipper
223
+
224
+ until zipper.last?
225
+ zipper = zipper.next
226
+ children << zipper
227
+ end
228
+ end
229
+
230
+ children
231
+ end
232
+
233
+ # Recursively descend to each node's `nth` child
234
+ #
235
+ # @return [AbstractCursor]
236
+ def descendant(n, *ns)
237
+ cursor = self
238
+
239
+ n.cons(ns).each do |n|
240
+ cursor = cursor.child(n)
241
+ end
242
+
243
+ cursor
244
+ end
245
+
246
+ # Navigate to the parent node
247
+ #
248
+ # @return [AbstractCursor]
249
+ # abstract :up
250
+
251
+ # Navigate to the next (rightward) sibling node
252
+ #
253
+ # @return [AbstractCursor]
254
+ # abstract :next
255
+
256
+ # Navigate to the previous (leftward) sibling node
257
+ #
258
+ # @return [AbstractCursor]
259
+ # abstract :prev
260
+
261
+ # Navigate to the first (leftmost) sibling node
262
+ #
263
+ # @return [AbstractCursor]
264
+ # abstract :first
265
+
266
+ # Navigate to the last (rightmost) sibling node
267
+ #
268
+ # @return [AbstractCursor]
269
+ # abstract :last
270
+
271
+ # Navigate to the root node
272
+ #
273
+ # @return [RootCursor]
274
+ def root
275
+ cursor = self
276
+ cursor = cursor.up until cursor.root?
277
+ cursor
278
+ end
279
+
280
+ # @group Editing the Tree
281
+ #########################################################################
282
+
283
+ # Insert a new sibling node after (to the right of) the current node,
284
+ # and navigate to the new sibling node
285
+ #
286
+ # @return [EditedCursor]
287
+ # abstract :append, :args => %w(sibling)
288
+
289
+ # Insert a new sibling node before (to the left of) the current node,
290
+ # and navigate to the new sibling node
291
+ #
292
+ # @return [EditedCursor]
293
+ # abstract :prepend, :args => %w(sibling)
294
+
295
+ # (see #append)
296
+ def insert_right(sibling)
297
+ append(sibling)
298
+ end
299
+
300
+ # (see #prepend)
301
+ def insert_left(sibling)
302
+ prepend(sibling)
303
+ end
304
+
305
+ # Insert a new child node before (to the left of) any existing children
306
+ # nodes and navigate to the new child node
307
+ #
308
+ # @return [EditedCursor]
309
+ def prepend_child(child)
310
+ if node.leaf?
311
+ raise Errors::ZipperError,
312
+ "cannot add child to leaf node"
313
+ end
314
+
315
+ EditedCursor.new(child,
316
+ Hole.new([], path, node.children), self)
317
+ end
318
+
319
+ # Insert a new child node after (to the right of) any existing children
320
+ # nodes and navigate to the new child node
321
+ #
322
+ # @return [EditedCursor]
323
+ def append_child(child)
324
+ if node.leaf?
325
+ raise Errors::ZipperError,
326
+ "cannot add child to leaf node"
327
+ end
328
+
329
+ EditedCursor.new(child,
330
+ Hole.new(node.children.reverse, path, []), self)
331
+ end
332
+
333
+ # Replace the current node with the given node
334
+ #
335
+ # @return [AbstractCursor]
336
+ # abstract :replace, :args => %w(node)
337
+
338
+ # Remove the current node, and navigate to the next (rightward) node if
339
+ # one exists. Otherwise, navigate to the previous (leftward) node if one
340
+ # exists. Otherwise, create a placeholder where the next sibling node will
341
+ # be created.
342
+ #
343
+ # @return [EditedCursor]
344
+ # abstract :delete
345
+
346
+ # @endgroup
347
+ #########################################################################
348
+ end
349
+
350
+ end
351
+ end