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