plan.org describes containers, pools and classes as they are after the repeal

This commit is contained in:
Joseph Ferano 2026-09-25 07:37:37 +07:00
parent cf71caefc2
commit 918edfb0b5
2 changed files with 27 additions and 21 deletions

View File

@ -1966,9 +1966,13 @@ CLOSED: [2026-09-25]
The five predicates are the checker's five, with =integer?= in place of
=copyable?=, and the section no longer names =(Handle $t)= or =pool-new=.
** TODO plan.org's Data model section still describes move-only containers
"Owning containers move rather than copy on assignment" and "one =Vec= field
makes it move-only" predate the repeal, under which everything copies.
** DONE plan.org's Data model section still describes move-only containers
CLOSED: [2026-09-25]
The Data model section says assignment copies a container's header and the
copies alias one buffer. The memory tiers and the classes section name the pool
and generational handles as a library over a =Vec=, and the classes gate that
named =Handle= is replaced by what classes are as built. The milestone record
of what was frozen is left as history.
** DONE plan.org cites the wrong mechanism for jank's relinking bug
CLOSED: [2026-09-25]

View File

@ -5,7 +5,7 @@
Two documents are normative and are settled ahead of implementation. Anything in
this plan that contradicts them is out of date.
- [[file:spec-memory.md][spec-memory.md]] — ownership, the four container types,
copies and moves, assignable places, generics without type classes, function
copies, assignable places, generics without type classes, function
values.
- [[file:spec-conditions.md][spec-conditions.md]] — the six hard cases of
conditions/restarts: what ~signal~ returns, no-handler behaviour, restart
@ -56,14 +56,15 @@ Allocators all the way down; ~malloc~ hidden behind them.
| Tier | Strategy | Cost |
|-----------+---------------------------------+----------------|
| Frame | arena, bulk reset each frame | free |
| Entities | pool + generational handles | free |
| Entities | a pool over a ~Vec~, in a library | free |
| Subsystem | region, freed wholesale | free |
| Dev/REPL | leaks by design, reset on reload| dev only |
- Allocator is part of the calling convention, so a refcounted allocator can be
added later without a language change.
- Generational handles instead of pointers for cross-references: a stale
reference is detectable, not undefined behaviour.
- Generational handles instead of pointers for cross-references, written as a
library over a ~Vec~ rather than provided by the language: a stale reference
is detectable, not undefined behaviour.
- Symbols and code live in a permanent arena that only grows.
** Why no persistent collections
@ -93,8 +94,8 @@ world.
|-------------+-----------------+------------------+------|
| ~[n T]~ | inline, n items | copies | no |
| ~[T]~ | ptr+len | copies the view | no |
| ~(Vec T)~ | ptr+len+cap | *moves* | yes |
| ~(Map K V)~ | open addressing | *moves* | yes |
| ~(Vec T)~ | ptr+len+cap | copies the header | yes |
| ~(Map K V)~ | open addressing | copies the header | yes |
~Vec~ and ~Map~ are monomorphic on element type and record their allocator. Not
a Lua-style array/hash hybrid — that is what makes Lua's layout and performance
unpredictable.
@ -105,17 +106,15 @@ world.
returns ~(Option V)~; ~put~ is the ~()~-returning upsert. See
spec-memory.md for the deferred move-aware operations.
- Operations: ~get~, ~put~, ~remove~, ~push~, ~pop~, ~at~, ~length~, ~update~.
Copying is explicit: ~(clone m)~, and owning containers move rather than copy on
assignment. No ~!~ convention — nothing is immutable, so it
An independent copy is explicit: ~(clone m)~. Assignment copies a container's
header, and the two headers alias one buffer. No ~!~ convention — nothing is immutable, so it
would carry no information. No ~assoc~; it only existed as the copy-returning form.
- ~const~ qualifier on references and slices: compile-time contract that a
callee will not mutate. Zero runtime cost.
- Value structs copy on assignment — but only *value* structs. Ownership is
structural: a struct is a value type iff every field is, so one ~Vec~ field
makes it move-only. This is what keeps "copies on assignment" from meaning a
shallow copy that aliases owned storage. Deep copies are always explicit:
~(clone x)~. Value structs are the snapshot / undo / replay story; they need no
separate type.
- Structs copy on assignment. A struct with a ~Vec~ field copies the header, so
the two copies alias one buffer, as in Odin. Deep copies are always explicit:
~(clone x)~. Structs without owning fields are the snapshot / undo / replay
story; they need no separate type.
- Literals live in read-only memory.
- *A global's initialiser may be computed.* A value the linker can write goes
into the image and costs nothing to start; anything else is stored at
@ -156,8 +155,8 @@ identity, extensibility, and live schema changes:
Classes require a managed allocation strategy and runtime class/shape metadata,
but *not necessarily a tracing GC*. The initial likely choices are a world or
session arena, pool allocation behind generational ~(Handle T)~ values, or an
explicitly owned region. A small tracing GC confined to class instances remains
session arena, a library pool behind generational handles, or an explicitly
owned region. A small tracing GC confined to class instances remains
an option if cyclic graphs prove burdensome; it never changes ~struct~ layout or
the C ABI.
@ -187,8 +186,11 @@ surprising lazy mutation on field access:
#+end_src
The precise class syntax, inheritance, storage strategy, and migration API are
not frozen. Do not add classes until ordinary ~struct~, ~Handle~, and reload
semantics are working.
not frozen. What is built is narrower than this section: a ~defclass~ instance
is a dyn map with its class in the object header, and a redefined class
migrates each instance lazily at its next touch, so nothing enumerates live
instances (TODO.org, "defclass is a named dyn map with a shape tag" and "A
redefined defclass migrates its instances lazily").
Immutability also serves the optimiser: a value known never to be mutated can be
copied into registers and stack-allocated freely. Mutability is what forces heap