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,107 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class DanglingCursor < AbstractCursor
5
+
6
+ # @return [AbstractCursor]
7
+ attr_reader :parent
8
+
9
+ def initialize(parent)
10
+ @parent = parent
11
+ end
12
+
13
+ # (see AbstractCursor#node)
14
+ def node
15
+ raise Errors::ZipperError,
16
+ "DanglingCursor#node should not be called"
17
+ end
18
+
19
+ # (see AbstractCursor#path)
20
+ def path
21
+ Hole.new([], @parent.path.parent, [])
22
+ end
23
+
24
+ # @group Querying the Tree Location
25
+ #########################################################################
26
+
27
+ # (see AbstractCursor#leaf?)
28
+ def leaf?
29
+ true
30
+ end
31
+
32
+ # (see AbstractCursor#root?)
33
+ def root?
34
+ false
35
+ end
36
+
37
+ # (see AbstractCursor#depth)
38
+ def depth
39
+ @parent.depth + 1
40
+ end
41
+
42
+ # (see AbstractCursor#first?)
43
+ def first?
44
+ true
45
+ end
46
+
47
+ # (see AbstractCursor#last?)
48
+ def last?
49
+ true
50
+ end
51
+
52
+ # @group Traversing the Tree
53
+ #########################################################################
54
+
55
+ # @return [AbstractCursor]
56
+ def up
57
+ @parent
58
+ end
59
+
60
+ # (see AbstractCursor#next)
61
+ def next
62
+ raise Errors::ZipperError,
63
+ "cannot move to next after last node"
64
+ end
65
+
66
+ # (see AbstractCursor#prev)
67
+ def prev
68
+ raise Errors::ZipperError,
69
+ "cannot move to prev before first node"
70
+ end
71
+
72
+ # (see AbstractCursor#first)
73
+ def first
74
+ self
75
+ end
76
+
77
+ # (see AbstractCursor#last)
78
+ def last
79
+ self
80
+ end
81
+
82
+ # @group Editing the Tree
83
+ #########################################################################
84
+
85
+ # (see AbstractCursor#replace)
86
+ def replace(node)
87
+ @parent.append_child(node)
88
+ end
89
+
90
+ alias prepend replace
91
+ alias append replace
92
+
93
+ # (see AbstractCursor#delete)
94
+ def delete
95
+ self
96
+ end
97
+
98
+ # @endgroup
99
+ #########################################################################
100
+
101
+ def inspect
102
+ "Cursor(..., ___)"
103
+ end
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,161 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class EditedCursor < AbstractCursor
5
+
6
+ # (see AbstractCursor#node)
7
+ attr_reader :node
8
+
9
+ # @return [Hole]
10
+ attr_reader :path
11
+
12
+ # @return [AbstractCursor]
13
+ attr_reader :parent
14
+
15
+ def initialize(node, path, parent)
16
+ @node, @path, @parent =
17
+ node, path, parent
18
+ end
19
+
20
+ # @group Querying the Tree Location
21
+ #########################################################################
22
+
23
+ # (see AbstractCursor#leaf?)
24
+ def leaf?
25
+ @node.leaf? or @node.children.empty?
26
+ end
27
+
28
+ # (see AbstractCursor#root?)
29
+ def root?
30
+ false
31
+ end
32
+
33
+ # @group Traversing the Tree
34
+ #########################################################################
35
+
36
+ # (see AbstractCursor#up)
37
+ # @return [AbstractCursor]
38
+ def up
39
+ node =
40
+ @parent.node.copy(children:
41
+ @path.left.reverse.concat(@node.cons(@path.right)))
42
+
43
+ if @parent.root?
44
+ RootCursor.new(node)
45
+ else
46
+ EditedCursor.new(node, @path.parent, @parent.parent)
47
+ end
48
+ end
49
+
50
+ # (see AbstractCursor#next)
51
+ # @return [EditedCursor]
52
+ def next
53
+ if last?
54
+ raise Errors::ZipperError,
55
+ "cannot move to next after last node"
56
+ end
57
+
58
+ head, *tail = @path.right
59
+
60
+ EditedCursor.new(head,
61
+ Hole.new(@node.cons(@path.left), @path.parent, tail), @parent)
62
+ end
63
+
64
+ # (see AbstractCursor#prev)
65
+ # @return [EditedCursor]
66
+ def prev
67
+ if first?
68
+ raise Errors::ZipperError,
69
+ "cannot move to prev before first node"
70
+ end
71
+
72
+ head, *tail = @path.left
73
+
74
+ EditedCursor.new(head,
75
+ Hole.new(tail, @path.parent, @node.cons(@path.right)), @parent)
76
+ end
77
+
78
+ # (see AbstractCursor#first)
79
+ # @return [EditedCursor]
80
+ def first
81
+ if first?
82
+ return self
83
+ end
84
+
85
+ right = @path.left.init.reverse.concat(@node.cons(@path.right))
86
+
87
+ EditedCursor.new(@path.left.last,
88
+ Hole.new([], @path.parent, right), @parent)
89
+ end
90
+
91
+ # (see AbstractCursor#last)
92
+ # @return [EditedCursor]
93
+ def last
94
+ if last?
95
+ return self
96
+ end
97
+
98
+ left = @node.cons(@path.right.init.reverse).concat(@path.left)
99
+
100
+ EditedCursor.new(@path.right.last,
101
+ Hole.new(left, @path.parent, []), @parent)
102
+ end
103
+
104
+ # @group Editing the Tree
105
+ #########################################################################
106
+
107
+ # (see AbstractCursor#last)
108
+ # @return [EditedCursor]
109
+ def append(node)
110
+ EditedCursor.new(node,
111
+ Hole.new(@node.cons(@path.left), @path.parent, @path.right), @parent)
112
+ end
113
+
114
+ # (see AbstractCursor#last)
115
+ # @return [EditedCursor]
116
+ def prepend(node)
117
+ EditedCursor.new(node,
118
+ Hole.new(@path.left, @path.parent, @node.cons(@path.right)), @parent)
119
+ end
120
+
121
+ # (see AbstractCursor#last)
122
+ # @return [EditedCursor]
123
+ def replace(node)
124
+ EditedCursor.new(node, @path, @parent)
125
+ end
126
+
127
+ # (see AbstractCursor#last)
128
+ # @return [EditedCursor]
129
+ def delete
130
+ if not last?
131
+ # Move to `next`
132
+ head, *tail = @path.right
133
+
134
+ EditedCursor.new(head,
135
+ Hole.new(@path.left, @path.parent, tail), @parent)
136
+ elsif not first?
137
+ # Move to `prev`
138
+ head, *tail = @path.left
139
+
140
+ EditedCursor.new(head,
141
+ Hole.new(tail, @path.parent, @path.right), @parent)
142
+ else
143
+ # Deleting the only child
144
+ parent =
145
+ @parent.node.copy(children:
146
+ @path.left.reverse.concat(@path.right))
147
+
148
+ EditedCursor.new(parent, @path.parent, @parent.parent).dangle
149
+ end
150
+ end
151
+
152
+ # @endgroup
153
+ #########################################################################
154
+
155
+ def inspect
156
+ "Cursor(..., #{@node.inspect})"
157
+ end
158
+ end
159
+
160
+ end
161
+ end
@@ -0,0 +1,129 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class MemoizedCursor < AbstractCursor
5
+
6
+ # @return [#leaf?, #children, #copy]
7
+ attr_reader :node
8
+
9
+ # @return [Hole]
10
+ attr_reader :path
11
+
12
+ # @return [AbstractCursor]
13
+ attr_reader :up
14
+
15
+ def initialize(node, path, up)
16
+ @node, @path, @up =
17
+ node, path, up
18
+ end
19
+
20
+ # @group Querying the Tree Location
21
+ #########################################################################
22
+
23
+ def leaf?
24
+ @node.leaf? or @node.children.empty?
25
+ end
26
+
27
+ def root?
28
+ false
29
+ end
30
+
31
+ # @group Traversing the Tree
32
+ #########################################################################
33
+
34
+ # @return [MemoizedCursor]
35
+ def next
36
+ @__next ||= begin
37
+ if last?
38
+ raise Errors::ZipperError,
39
+ "cannot move to next after last node"
40
+ end
41
+
42
+ head, *tail = @path.right
43
+
44
+ MemoizedCursor.new(head,
45
+ Hole.new(@node.cons(@path.left), @path.parent, tail), @up)
46
+ end
47
+ end
48
+
49
+ # @return [MemoizedCursor]
50
+ def prev
51
+ @__prev ||= begin
52
+ if first?
53
+ raise Errors::ZipperError,
54
+ "cannot move to prev before first node"
55
+ end
56
+
57
+ head, *tail = @path.left
58
+
59
+ MemoizedCursor.new(head,
60
+ Hole.new(tail, @path.parent, @node.cons(@path.right)), @up)
61
+ end
62
+ end
63
+
64
+ # @return [MemoizedCursor]
65
+ def first
66
+ @up.down
67
+ end
68
+
69
+ # @return [MemoizedCursor]
70
+ def last
71
+ current = self
72
+ current = current.next until current.last?
73
+ current
74
+ end
75
+
76
+ # @group Editing the Tree
77
+ #########################################################################
78
+
79
+ # @return [EditedCursor]
80
+ def append(node)
81
+ EditedCursor.new(node,
82
+ Hole.new(@node.cons(@path.left), @path.parent, @path.right), @up)
83
+ end
84
+
85
+ # @return [EditedCursor]
86
+ def prepend(node)
87
+ EditedCursor.new(node,
88
+ Hole.new(@path.left, @path.parent, @node.cons(@path.right)), @up)
89
+ end
90
+
91
+ # @return [EditedCursor]
92
+ def replace(node)
93
+ EditedCursor.new(node, @path, @up)
94
+ end
95
+
96
+ # @return [EditedCursor]
97
+ def delete
98
+ if not last?
99
+ # Move to `next`
100
+ head, *tail = @path.right
101
+
102
+ EditedCursor.new(head,
103
+ Hole.new(@path.left, @path.parent, tail), @up)
104
+ elsif not first?
105
+ # Move to `prev`
106
+ head, *tail = @path.left
107
+
108
+ EditedCursor.new(head,
109
+ Hole.new(tail, @path.parent, @path.right), @up)
110
+ else
111
+ # Deleting the only child
112
+ parent =
113
+ @up.node.copy(children:
114
+ @path.left.reverse.concat(@path.right))
115
+
116
+ EditedCursor.new(parent, @path.parent, @up.parent).dangle
117
+ end
118
+ end
119
+
120
+ # @endgroup
121
+ #########################################################################
122
+
123
+ def inspect
124
+ "Cursor(..., #{@node.inspect})"
125
+ end
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,57 @@
1
+ module Fr
2
+ module TZipper
3
+
4
+ class Node
5
+
6
+ attr_reader :value
7
+
8
+ attr_reader :children
9
+
10
+ def initialize(value, children = [])
11
+ @value, @children =
12
+ value, children
13
+ end
14
+
15
+ def copy(changes = {})
16
+ Node.new \
17
+ changes.fetch(:value, @value),
18
+ changes.fetch(:children, @value)
19
+ end
20
+
21
+ # Allow any node to have children
22
+ def leaf?
23
+ false
24
+ end
25
+
26
+ # This is implemented in object.rb
27
+ def cons(tail = [])
28
+ [self] + tail
29
+ end
30
+
31
+ def inspect
32
+ if @children.empty?
33
+ "Leaf(#{@value.inspect})"
34
+ else
35
+ "Node(#{@value.inspect}, #{@children.map(&:inspect).join(", ")})"
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ class << Node
42
+
43
+ def build(value, children)
44
+ Node.new(value, children.map do |c|
45
+ if Array === c
46
+ value_, *children_ = c
47
+ Node.build(value_, children_)
48
+ else
49
+ Node.new(c)
50
+ end
51
+ end)
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+ end