On Jun 10, 2016, at 10:55, Corbin Simpson <
mostawe...@gmail.com> wrote:
>
> Hi everybody! I'm Corbin. I haven't posted here before. I'm working on Monte.
Hey! Thanks for writing txWS! </irrelevant>
> In Monte, we've explored efficient representations for our collections. The ConstMap has been a nemesis in this effort, because its list of requirements is very onerous:
>
> * Persistent/immutable/COW/structually-sharing. Whatever you want to call it. Y'all know what I'm talking about.
> * Iterable with a consistent and predictable iteration order based on insertion order. This is the hardest one, because it implies...
> * It's fine to require hashable (settled) keys, but not sorted keys. And you can't rely on hashing order.
> * Quick membership test/lookup. Amortized O(1) preferred, but I'll settle for O(log n).
>
> I'm currently waffling between two options. The first option is cutting-edge HAMTs. The problem with HAMTs is that the hashing has to be consistent between vats if you want replay to work across vats.
This sounds like you are assuming a too-weak iteration order property.
1. Replay necessitates deterministic behavior,
2. which results from fully-specified operations,
3. which includes that ordering is derived only from observable properties,
4. and non-Data† objects do not have observable hash codes,
so your hashing strategy cannot interfere with replay.
† Data = a more well-founded notion of DeepPassByCopy.
> The other option I've been thinking about is ordered dictionaries, as in Python. Monte's built on the RPython toolchain, and we have an RPython-level ordered dictionary for free. It has the insertion-order behavior that we want, and it's quite speedy. Unfortunately, it's not at all persistent, so we have to do lots and lots of copies. It *is* a great FlexMap structure, but not a great ConstMap structure.
>
> I've been reading a bunch but haven't found anything good, and the E docs don't indicate that a good solution was ever found. Has there been some research that I haven't yet found?
In E-on-CL, I implemented a special case for the common pattern of accumulating a ConstMap or ConstList which seemed to work fairly well without having the trickiness of persistent data structures. (Apologies if my E syntax is wrong; it's been a while.)
The pattern is that if x is a ConstMap then the method x.with(k, v) does not construct a ConstMap. Instead, it constructs a ref -- you could call it a thunk ref or a lazy ref -- which internally stores [x, k, v]. The same thing applies if you call with/2 again on its result. So you get a data structure shaped like a linked list: [[x, k1, v1], k2, v2].
The behavior of this thunk, then, is that if you do anything whatsoever on it _except_ calling with/2 again, it turns itself into a normal ref to a reified ConstMap, in a monolithic operation on the original map & _all_ added entries.
This ensures that with-accumulation is O(n) rather than O(n^2), but does nothing for any other pattern of use.
Here's links into the code; the relevant classes are with-node and lazy-ref.
https://github.com/kpreid/e-on-cl/blob/f93d188051c66db0ad4ff150bd73b838f7bc25ed/lisp/tables2.lisp#L590
https://github.com/kpreid/e-on-cl/blob/f93d188051c66db0ad4ff150bd73b838f7bc25ed/lisp/lazy.lisp