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,62 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class EditedCursor < AbstractCursor
5
+ attr_reader :node
6
+
7
+ def initialize(node, prev)
8
+ @node, @prev =
9
+ node, prev
10
+ end
11
+
12
+ def head?
13
+ false
14
+ end
15
+
16
+ def tail?
17
+ @node.tail?
18
+ end
19
+
20
+ def prev(phantom = false)
21
+ if @prev.head?
22
+ HeadCursor.new(@prev.node.copy(next: @node))
23
+ else
24
+ EditedCursor.new(@prev.node.copy(next: @node), @prev.prev)
25
+ end
26
+ end
27
+
28
+ def append(node)
29
+ EditedCursor.new(node.copy(next: @node.next), self)
30
+ end
31
+
32
+ def prepend(node)
33
+ EditedCursor.new(node.copy(next: @node), prev)
34
+ end
35
+
36
+ def update(node = nil)
37
+ if node.nil?
38
+ EditedCursor.new(yield(@node).copy(next: @node.next), @prev)
39
+ else
40
+ EditedCursor.new(node.copy(next: @node.next), @prev)
41
+ end
42
+ end
43
+
44
+ def replace(node)
45
+ EditCursor.new(node, @prev)
46
+ end
47
+
48
+ def delete
49
+ if @prev.head?
50
+ HeadCursor.new(prev.node.copy(next: @node.next))
51
+ else
52
+ EditedCursor.new(prev.node.copy(next: @node.next), @prev.prev)
53
+ end
54
+ end
55
+
56
+ def inspect
57
+ "Cursor([..., #{@node.inspect}, ...])"
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,266 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ module Enumerable
5
+
6
+ def each
7
+ cursor = self
8
+ yield(cursor)
9
+
10
+ until cursor.tail?
11
+ cursor = cursor.next
12
+ yield(cursor)
13
+ end
14
+
15
+ self
16
+ end
17
+
18
+ def all?
19
+ catch(:done) do
20
+ each{|c| throw(:done, false) if !yield(c.node) }; true
21
+ end
22
+ end
23
+
24
+ def any?
25
+ catch(:done) do
26
+ each{|c| throw(:done, true) if yield(c.node) }; false
27
+ end
28
+ end
29
+
30
+ def chunk
31
+ # todo: defined?
32
+ end
33
+
34
+ def collect
35
+ unless tail?
36
+ current = yield(node)
37
+ next_ = Fr.thunk { self.next.collect{|n| yield(n) }.node }
38
+ replace(current.copy(next: next_))
39
+ else
40
+ update(yield(node))
41
+ end
42
+ end
43
+
44
+ def collect_concat
45
+ # todo: defined?
46
+ end
47
+
48
+ def count
49
+ foldl(0){|count, c| yield(c.node) ? count + 1 : count }
50
+ end
51
+
52
+ def cycle
53
+ # todo: defined?
54
+ end
55
+
56
+ def detect(default = nil)
57
+ catch(:done) do
58
+ each{|c| throw(:done, c) if yield(c.node) }; default
59
+ end
60
+ end
61
+
62
+ def drop(n)
63
+ # Seek n nodes forward
64
+ nth = n.times.inject(self.next(true)){|c,_| c.next(true) }
65
+ mth = node.copy(next: nth.prev.node.next)
66
+ replace(mth)
67
+ end
68
+
69
+ def drop_while
70
+ # todo
71
+ end
72
+
73
+ def entries
74
+ foldl([]){|entries, c| entries << c.node }
75
+ end
76
+
77
+ alias_method :find, :detect
78
+
79
+ def filter
80
+ node = filter_{|n| yield(n) }
81
+ unless node.nil?
82
+ replace(node)
83
+ else
84
+ prev(true).truncate
85
+ end
86
+ end
87
+
88
+ # @private
89
+ def filter_
90
+ unless tail?
91
+ Fr.thunk do
92
+ if yield(node)
93
+ node.copy(next: self.next.filter_{|n| yield(n) })
94
+ else
95
+ self.next.filter_{|n| yield(n) }
96
+ end
97
+ end
98
+ else
99
+ yield(node) ?
100
+ node : nil
101
+ end
102
+ end
103
+
104
+ alias_method :find_all, :filter
105
+
106
+ def index(default = nil)
107
+ position_ = position
108
+
109
+ catch(:done) do
110
+ each do |c|
111
+ if yield(c.node)
112
+ throw(:done, position_)
113
+ else
114
+ position_ += 1
115
+ end
116
+ end
117
+
118
+ default
119
+ end
120
+ end
121
+
122
+ alias_method :find_index, :index
123
+
124
+ # alias_method :first, :head
125
+ def first(n = 1)
126
+ if n == 1
127
+ head
128
+ else
129
+ # todo: defined?
130
+ end
131
+ end
132
+
133
+ def flat_map
134
+ # todo
135
+ end
136
+
137
+ def grep(pattern)
138
+ filter{|node| pattern === node }
139
+ end
140
+
141
+ def group_by
142
+ # todo: defined?
143
+ end
144
+
145
+ def include?(needle)
146
+ any?{|node| node == needle }
147
+ end
148
+
149
+ def foldl(svalue)
150
+ cursor = self
151
+ svalue = yield(svalue, cursor)
152
+ until cursor.tail?
153
+ cursor = cursor.next
154
+ svalue = yield(svalue, cursor)
155
+ end
156
+ svalue
157
+ end
158
+
159
+ alias_method :inject, :foldl
160
+
161
+ alias_method :map, :collect
162
+
163
+ def max
164
+ reduce{|a,b| a.node < b.node ? b : a }
165
+ end
166
+
167
+ def max_by
168
+ reduce{|a,b| yield(a.node) < yield(b.node) ? b : a }
169
+ end
170
+
171
+ def min
172
+ reduce{|a,b| a.node > b.node ? b : a }
173
+ end
174
+
175
+ def min_by
176
+ reduce{|a,b| yield(a.node) > yield(b.node) ? b : a }
177
+ end
178
+
179
+ def minmax
180
+ foldl([self, self]) do |(min,max), c|
181
+ [min.node > c.node ? c : min,
182
+ max.node < c.node ? c : max]
183
+ end
184
+ end
185
+
186
+ def minmax_by
187
+ foldl([self, self]) do |(min,max), c|
188
+ [yield(min.node) > yield(c.node) ? c : min,
189
+ yield(max.node) < yield(c.node) ? c : max]
190
+ end
191
+ end
192
+
193
+ def none?
194
+ catch(:done) do
195
+ each{|c| throw(:done, false) if yield(c.node) }; true
196
+ end
197
+ end
198
+
199
+ alias_method :one?, :any?
200
+
201
+ def partition
202
+ # todo
203
+ end
204
+
205
+ def reduce(op = nil)
206
+ # [].reduce(op) #=> nil
207
+ # [a].reduce(op) #=> a
208
+ # [a,b].reduce(op) #=> a op b
209
+ # [a,b,c].reduce(op) #=> a op b op c
210
+
211
+ svalue = self
212
+ cursor = self
213
+
214
+ unless op.nil?
215
+ until cursor.tail?
216
+ cursor = cursor.next
217
+ svalue = svalue.send(op, cursor)
218
+ end
219
+ else
220
+ until cursor.tail?
221
+ cursor = cursor.next
222
+ svalue = yield(svalue, cursor)
223
+ end
224
+ end
225
+
226
+ svalue
227
+ end
228
+
229
+ # todo:
230
+ # - api: this always returns a cursor pointed at tail (ugh)
231
+ # - api: should be lazy
232
+ # - bug: doesn't filter tail element
233
+ def reject
234
+ cursor = self
235
+ until cursor.tail?
236
+ cursor = yield(cursor.node) ?
237
+ cursor.replace(cursor.node.next) :
238
+ cursor.next
239
+ end
240
+ cursor
241
+ end
242
+
243
+ alias_method :select, :filter
244
+
245
+ def sort
246
+ # todo: defined?
247
+ end
248
+
249
+ def take(n)
250
+ # todo: defined?
251
+ end
252
+
253
+ def take_while
254
+ # todo: defined?
255
+ end
256
+
257
+ alias_method :to_a, :entries
258
+
259
+ def zip(stream, *streams)
260
+ # todo: should be lazy
261
+ end
262
+
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,71 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class HeadCursor < AbstractCursor
5
+ # @return [#next, #tail?]
6
+ attr_reader :node
7
+
8
+ def initialize(node)
9
+ @node = node
10
+ end
11
+
12
+ def position
13
+ 0
14
+ end
15
+
16
+ def head?
17
+ true
18
+ end
19
+
20
+ def tail?
21
+ @node.tail?
22
+ end
23
+
24
+ def head
25
+ self
26
+ end
27
+
28
+ def prev(phantom = false)
29
+ if phantom
30
+ HeadPrevCursor.new(@node)
31
+ else
32
+ raise Errors::ZipperError,
33
+ "head node has no prev"
34
+ end
35
+ end
36
+
37
+ def append(node)
38
+ EditedCursor.new(node.copy(next: @node.next), self)
39
+ end
40
+
41
+ def prepend(node)
42
+ HeadCursor.new(node.copy(next: @node))
43
+ end
44
+
45
+ def update(node = nil)
46
+ if node.nil?
47
+ HeadCursor.new(yield(@node).copy(next: @node.next))
48
+ else
49
+ HeadCursor.new(node.copy(next: @node.next))
50
+ end
51
+ end
52
+
53
+ def replace(node)
54
+ HeadCursor.new(node)
55
+ end
56
+
57
+ def delete
58
+ if tail?
59
+ HeadPrevCursor.new(nil)
60
+ else
61
+ HeadCursor.new(@node.next)
62
+ end
63
+ end
64
+
65
+ def inspect
66
+ "Cursor([#{@node.inspect}, ...])"
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,112 @@
1
+ module Fr
2
+ module SZipper
3
+
4
+ class HeadPrevCursor < AbstractCursor
5
+ def initialize(node)
6
+ @node = node
7
+ end
8
+
9
+ def position
10
+ -1
11
+ end
12
+
13
+ def head?
14
+ @node.nil?
15
+ end
16
+
17
+ def tail?
18
+ @node.nil?
19
+ end
20
+
21
+ def empty?
22
+ @node.nil?
23
+ end
24
+
25
+ def head
26
+ if @node.nil?
27
+ raise Errors::ZipperError,
28
+ "head.prev has no head"
29
+ else
30
+ HeadCursor.new(@node)
31
+ end
32
+ end
33
+
34
+ def tail
35
+ if @node.nil?
36
+ raise Errors::ZipperError,
37
+ "head.prev has no tail"
38
+ else
39
+ HeadCursor.new(@node).tail
40
+ end
41
+ end
42
+
43
+ def prev(phantom = false)
44
+ if phantom
45
+ self
46
+ else
47
+ raise Errors::ZipperError,
48
+ "head.prev has no prev"
49
+ end
50
+ end
51
+
52
+ def next(phantom = false)
53
+ if tail?
54
+ if phantom
55
+ self
56
+ else
57
+ raise Errors::ZipperError,
58
+ "head.prev has no next"
59
+ end
60
+ else
61
+ HeadCursor.new(@node)
62
+ end
63
+ end
64
+
65
+ def append(node)
66
+ HeadCursor.new(node.copy(next: @node))
67
+ end
68
+
69
+ def prepend(node)
70
+ HeadCursor.new(node.copy(next: @node))
71
+ end
72
+
73
+ def update(node = nil)
74
+ if node.nil?
75
+ HeadCursor.new(yield(nil).copy(next: @node))
76
+ else
77
+ HeadCursor.new(node.copy(next: @node))
78
+ end
79
+ end
80
+
81
+ def replace(node)
82
+ HeadCursor.new(node)
83
+ end
84
+
85
+ def delete
86
+ if tail?
87
+ self
88
+ else
89
+ HeadCursor.new(@node)
90
+ end
91
+ end
92
+
93
+ def truncate
94
+ HeadPrevCursor.new(nil)
95
+ end
96
+
97
+ def inspect
98
+ if tail?
99
+ "Cursor([___])"
100
+ else
101
+ "Cursor([___, #{@node.inspect}, ...])"
102
+ end
103
+ end
104
+
105
+ def ==(other)
106
+ HeadPrevCursor === other and
107
+ not empty? ^ other.empty?
108
+ end
109
+ end
110
+
111
+ end
112
+ end