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