The colon belongs to keys, and Map follows Odin rather than Clojure
This commit is contained in:
parent
eb98c5859a
commit
5badf6658b
43
NEXT.md
43
NEXT.md
@ -267,6 +267,49 @@ this closes nothing off. That section stays open for the record but is no longer
|
||||
~~**5. File I/O — `slurp` and `barf` — is the next stdlib work**~~ **Built.** It was the next stdlib work, after `Vec`, because `slurp` returns a string whose
|
||||
length is not known until the file is read and therefore cannot exist before an allocator does.
|
||||
|
||||
## Decided later the same day, and queued
|
||||
|
||||
**6. A field label is written with a dot, not a colon, and the colon is reserved for keys.** `{.x 1.0 .y 2.0}`
|
||||
replaces `{:x 1.0 :y 2.0}` in struct construction, and the same change applies where destructuring names a field.
|
||||
The delimiter is what makes it unambiguous, which is the author's argument and it holds: `(.x v)` is a call and
|
||||
therefore an access, `{.x 1.0}` is a brace form and therefore a construction. Today the dot already means "read a
|
||||
field" and the colon means both "field label" and "enum member", which is the ambiguity the change removes.
|
||||
|
||||
**The reason to do it before `Map`, not after.** A map literal will want to be `{:key value}`. If struct
|
||||
construction owns that exact spelling, map literals and struct literals are the same syntax and the checker has to
|
||||
tell them apart from context. Reserving the colon for keys — map keys and enum members — keeps the two visibly
|
||||
distinct. Doing this after `Map` lands means changing both; doing it now means changing one.
|
||||
|
||||
Cost: ~284 sites across 45 `.flan` files, mechanical. **Queued rather than started** only because it touches nearly
|
||||
every Flan file in the repo, including ones a running lane held. Run it when the tree is quiet, before step 4.
|
||||
|
||||
One thing to decide with it: destructuring uses the colon two ways — `{inner :field}` names a field, which should
|
||||
become a dot like any other field, and `{:keys [x y]}` where `:keys` is an instruction to the compiler rather than a
|
||||
field name, which arguably stays a colon. Settle both in the same pass rather than leaving the rule half-applied.
|
||||
|
||||
**7. `Map` follows Odin's implementation.** Read `base/runtime/dynamic_map_internal.odin` before writing any of it;
|
||||
the checkout is at `~/Repositories/Odin`. Three properties are the ones worth copying, and they are stated in its own
|
||||
header comment:
|
||||
|
||||
- **Open-addressed Robin Hood hashing at a 75% load factor.** No buckets, no per-entry allocation, and probe
|
||||
distances stay even because a later arrival steals a slot from an earlier one.
|
||||
- **Cache-line-aligned `Map_Cell` packing**, so no single key or value ever straddles a cache line and a linear probe
|
||||
walks memory in a cache-friendly order. This is the part a hand-rolled open-addressed map usually gets wrong.
|
||||
- **`uintptr` throughout** for sizes, masks and offsets, to keep sign-extension and masking instructions out of the
|
||||
probe loop.
|
||||
|
||||
Its static/dynamic split is the same type-erasure this project already committed to: `Map_Info` carries size,
|
||||
alignment and offsets, and the compiler emits the hash and equality pair per key type. `spec-memory.md`'s
|
||||
structural-key restriction holds this to the built-in key set, so there is no dispatch to design.
|
||||
|
||||
**Why this will not be Python's dict.** Worth recording because it is the question that prompted the decision.
|
||||
Python's dict algorithm is fine; what makes it slow is that every key and value is a separately allocated, reference-
|
||||
counted object, and hashing and comparison go through indirect calls that cannot be inlined. Flan stores raw bytes
|
||||
and compiles the hash and comparison concretely at each use. That difference is most of the gap before any
|
||||
algorithmic cleverness. **jank is not the model** — it is Clojure, so its maps are persistent with structural
|
||||
sharing, which plan.org rules out by name because shared structure destroys the clear ownership that is the whole
|
||||
reason there is no collector.
|
||||
|
||||
## Blocked and unfinished
|
||||
|
||||
Everything below was found, decided or half-built and then stopped. Each says what blocks it. Nothing here is a
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user