fr 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +0 -0
- data/Rakefile +81 -0
- data/lib/fr.rb +44 -0
- data/lib/fr/applicative.rb +0 -0
- data/lib/fr/array.rb +168 -0
- data/lib/fr/boolean.rb +15 -0
- data/lib/fr/either.rb +94 -0
- data/lib/fr/errors.rb +5 -0
- data/lib/fr/errors/zipper_error.rb +8 -0
- data/lib/fr/functor.rb +11 -0
- data/lib/fr/functor/array.rb +15 -0
- data/lib/fr/functor/either.rb +17 -0
- data/lib/fr/functor/maybe.rb +17 -0
- data/lib/fr/maybe.rb +76 -0
- data/lib/fr/monad.rb +236 -0
- data/lib/fr/monad/array.rb +21 -0
- data/lib/fr/monad/either.rb +18 -0
- data/lib/fr/monad/maybe.rb +24 -0
- data/lib/fr/monad/random.rb +50 -0
- data/lib/fr/monad/reader.rb +45 -0
- data/lib/fr/monad/state.rb +62 -0
- data/lib/fr/monad/writer.rb +49 -0
- data/lib/fr/monoid.rb +52 -0
- data/lib/fr/monoid/array.rb +13 -0
- data/lib/fr/monoid/boolean.rb +23 -0
- data/lib/fr/monoid/either.rb +13 -0
- data/lib/fr/monoid/maybe.rb +15 -0
- data/lib/fr/monoid/numeric.rb +31 -0
- data/lib/fr/monoid/string.rb +11 -0
- data/lib/fr/numeric.rb +1 -0
- data/lib/fr/object.rb +45 -0
- data/lib/fr/string.rb +1 -0
- data/lib/fr/szipper.rb +36 -0
- data/lib/fr/szipper/abstract_cursor.rb +63 -0
- data/lib/fr/szipper/edited_cursor.rb +62 -0
- data/lib/fr/szipper/enumerable.rb +266 -0
- data/lib/fr/szipper/head_cursor.rb +71 -0
- data/lib/fr/szipper/head_prev_cursor.rb +112 -0
- data/lib/fr/szipper/memoized_cursor.rb +59 -0
- data/lib/fr/szipper/node.rb +67 -0
- data/lib/fr/szipper/tail_next_cursor.rb +68 -0
- data/lib/fr/thunk.rb +73 -0
- data/lib/fr/tzipper.rb +38 -0
- data/lib/fr/tzipper/abstract_cursor.rb +351 -0
- data/lib/fr/tzipper/dangling_cursor.rb +107 -0
- data/lib/fr/tzipper/edited_cursor.rb +161 -0
- data/lib/fr/tzipper/memoized_cursor.rb +129 -0
- data/lib/fr/tzipper/node.rb +57 -0
- data/lib/fr/tzipper/path.rb +125 -0
- data/lib/fr/tzipper/root_cursor.rb +124 -0
- data/lib/fr/unfold.rb +93 -0
- data/spec/examples/array.example +0 -0
- data/spec/examples/monads/array.example +0 -0
- data/spec/examples/monads/maybe.example +0 -0
- data/spec/examples/monads/random.example +0 -0
- data/spec/examples/monads/state.example +0 -0
- data/spec/examples/szipper.example +652 -0
- data/spec/examples/thunk.example +0 -0
- data/spec/examples/tzipper.example +0 -0
- data/spec/examples/unfold.example +90 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/szipper/model.rb +225 -0
- data/spec/support/szipper/nodel.rb +144 -0
- 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
|