|
100 | 100 | // 2. We can't specify constraints on associated types. This forces many
|
101 | 101 | // trivial algorithms to specify useless constraints.
|
102 | 102 | //
|
| 103 | +// Solution: constraints on associated types are a desirable |
| 104 | +// language feature, part of the Swift generics model. This issue |
| 105 | +// will be fixed by compiler improvements. |
| 106 | +// |
103 | 107 | // Trees
|
104 | 108 | // =====
|
105 | 109 | //
|
|
150 | 154 | // reuse, then the tree nodes can't have parent pointers (a node
|
151 | 155 | // can have multiple different parents in different trees). This
|
152 | 156 | // means that it is not possible to advance an index in O(1). If
|
153 |
| -// we need to go up the tree while advancing the index, we would |
154 |
| -// need to traverse the tree starting from the root in O(log n). |
| 157 | +// we need to go up the tree while advancing the index, without |
| 158 | +// parent pointers we would need to traverse the tree starting from |
| 159 | +// the root in O(log n). |
| 160 | +// |
155 | 161 | // Thus, index has to essentially store a path through the tree
|
156 | 162 | // from the root to the node (it is usually possible to encode this
|
157 | 163 | // path in a 64-bit number). Since the index stores the path,
|
158 | 164 | // subscripting on such an index would also cost O(log n).
|
159 | 165 | //
|
160 |
| -// We should note that even though the operations mentioned cost |
161 |
| -// O(log n), the base of the logarithm would be typically large |
162 |
| -// (e.g., 32), and the size of the machine memory is limited. |
163 |
| -// Thus, we could treat the complexity as effectively constant for |
164 |
| -// all practical purposes. |
| 166 | +// We should note that persistent trees typically use B-trees, so |
| 167 | +// the base of the logarithm would be typically large (e.g., 32). |
| 168 | +// We also know that the size of the RAM is limited. Thus, we |
| 169 | +// could treat the O(log n) complexity as effectively constant for |
| 170 | +// all practical purposes. But the constant factor will be much |
| 171 | +// larger than in other designs. |
| 172 | +// |
| 173 | +// Swift's collection index model does not change anything as |
| 174 | +// compared to other languages. The important point is that the |
| 175 | +// proposed index model allows such implementations of persistent |
| 176 | +// collections. |
165 | 177 | //
|
166 | 178 | // 2. Trees with O(1) subscripting and advancing.
|
167 | 179 | //
|
|
208 | 220 | // extra data structure would only increase the constant factor
|
209 | 221 | // on memory overhead.
|
210 | 222 | //
|
| 223 | +// | (1) | (2)(a) | (2)(b) |
| 224 | +// -------------------------------------+----------+---------+------- |
| 225 | +// memory-safe | Yes | Yes | Yes |
| 226 | +// indices are not reference-counted | Yes | Yes | Yes |
| 227 | +// shares nodes | Yes | No | No |
| 228 | +// subscripting on an index | O(log n) | O(1) | O(1) |
| 229 | +// advancing an index | O(log n) | O(1) | O(1) |
| 230 | +// deleting does not invalidate indices | No | No | Yes |
| 231 | +// requires extra O(n) storage just ... | | | |
| 232 | +// for safety checks | No | No | Yes |
| 233 | +// |
211 | 234 | // Each of the designs discussed above has its uses, but the intuition
|
212 | 235 | // is that (2)(a) is the one most commonly needed in practice. (2)(a)
|
213 | 236 | // does not have the desired index invalidation properties. There is
|
214 | 237 | // a small number of commonly used algorithms that require that
|
215 | 238 | // property, and they can be provided as methods on the collection,
|
216 | 239 | // for example removeAll(in: Range<Index>) and
|
217 | 240 | // removeAll(_: (Element)->Bool).
|
| 241 | +// |
| 242 | +// If we were to allow reference-counted indices (basically, the |
| 243 | +// current collections model), then an additional design is possible |
| 244 | +// -- let's call it (2)(c) for the purpose of discussion. This design |
| 245 | +// would be like (2)(b), but won't require extra storage that is used |
| 246 | +// only for safety checks. Instead, every index would pay a RC |
| 247 | +// penalty and carry a strong reference to the tree node. |
| 248 | +// |
| 249 | +// Note that (2)(c) is still technically possible to implement in the |
| 250 | +// new collection index model, it just goes against a goal of having |
| 251 | +// indices free of reference-counted stored properties. |
218 | 252 |
|
219 | 253 | infix operator ...* { associativity none precedence 135 }
|
220 | 254 | infix operator ..<* { associativity none precedence 135 }
|
|
0 commit comments