diff --git a/FIX.org b/FIX.org index 6fd894d..4bd2388 100644 --- a/FIX.org +++ b/FIX.org @@ -151,7 +151,7 @@ against 985ms on LLVM. ** Open, carried forward - A transient signal -11 on the globals daemon, seen once, not reproduced. Recorded below. -- [drop]'s handoff flags that the [clone] / [get] / [map-next!] refusals are +- [drop]'s handoff flags that the [clone] / [get] / [map-next] refusals are needed by the arena route too — they are about a copy of a header, which an arena does not make safe — and that a Map has no operation answering *where* a value lives, which is what reading an arena-parsed EDN document back would diff --git a/NEXT.md b/NEXT.md index 2066b4b..5e88e35 100644 --- a/NEXT.md +++ b/NEXT.md @@ -550,7 +550,7 @@ constant folding has to accept that a length can stay symbolic until instantiati ### The motivating case, and it is not `$n` on its own -`$n` alone buys little. `swap!` does not need it — it takes a slice and two indices, and the length is a runtime +`$n` alone buys little. `swap` does not need it — it takes a slice and two indices, and the length is a runtime field. Nor does a `pop` from a `Vec`. The case that wants both is **a fixed-capacity array with a count and no allocation**, which is Odin's `Small_Array` and a good fit for a game that refuses to allocate in a frame: @@ -600,7 +600,7 @@ so `a + b` over a `$T` compiles and fails only when someone instantiates at a ty write a second pass to get plan.org's rule. Both options were bad in the way the other was good. Rejecting abstractly gives the error at the definition and -makes every call site carry a comparison: `(sort! xs)` becomes `(sort-by! xs (fn [a b] (< a b)))` everywhere. +makes every call site carry a comparison: `(sort xs)` becomes `(sort-by xs (fn [a b] (< a b)))` everywhere. Checking per instantiation keeps the call short and moves the error into code the caller did not write, which is worse here than in most languages because a hot-reload session may have been running for an hour before the call site is reached. @@ -608,7 +608,7 @@ site is reached. **What dissolves it is the thing plan.org ruled out while citing Odin: Odin has constraints.** `core/slice/slice.odin:289` is `where intrinsics.type_is_ordered(T)`, and there are 41 such predicates. A `where` clause tells the abstract pass what it may assume, so the body checks at the definition *and* the call stays -`(sort! xs)`. +`(sort xs)`. ### What is being built @@ -648,7 +648,7 @@ Measured by the spike against the real sources, and worth knowing before the wor - **5 do not collapse and should not** — `sum-i32`/`sum-f32` widen to `i64`/`f64` with an explicit cast, and "the wider type `t` accumulates into" is a type-level function, which is a constraint system or an associated type. A generic `sum` would have to take its accumulator and its `add`, at which point it *is* `reduce`. - `append-i64!`/`append-f64!` are two different primitives, `I64ToBytes` and `F64ToBytes`, and choosing between + `append-i64`/`append-f64` are two different primitives, `I64ToBytes` and `F64ToBytes`, and choosing between them per instantiation is compile-time overloading, which is what multimethods are for. The prelude keeps a per-type layer for the numeric ones. That is the honest number. @@ -1690,10 +1690,10 @@ allocation-free functions because there was nothing to allocate from; there are name" block at the foot of `prelude.ml` is down from eight entries to four, each with a *different* reason rather than the one shared sentence. -What landed: `append!`/`append-i64!`/`append-f64!` (the builder), `concat`, `join`, `split` returning a +What landed: `append`/`append-i64`/`append-f64` (the builder), `concat`, `join`, `split` returning a `(Vec [u8])`, `repeat-bytes`, `replace-bytes`, `to-lower`, `to-upper`, `slices-new`; `format-f64` with a precision; `atan2-f32` and `pow-f32`; `clamp` as a `defmacro`; and the slice family at two more element types — -`sort-f32!`, `reverse-f32!`, `swap-f32!`, `min-f32`, `max-f32`, `sum-f32`, `bytes`, `swap-bytes!`, `sort-bytes!`. +`sort-f32`, `reverse-f32`, `swap-f32`, `min-f32`, `max-f32`, `sum-f32`, `bytes`, `swap-bytes`, `sort-bytes`. Tests: `programs/strings.flan`, `programs/format.flan`, `programs/algorithms.flan`, `programs/math2.flan`. `string-from-bytes`, refused in that block, turned out to already exist: `string` is a builtin and @@ -1702,8 +1702,8 @@ Tests: `programs/strings.flan`, `programs/format.flan`, `programs/algorithms.fla **What could not be built, and why each one could not.** All four want a compiler or runtime change, and none of them wants a language decision. -- ~~**`Map` keys and values.**~~ **Iteration is built** — `flan_map_next` and the `map-next!` builtin, exactly the - shape this described. See [`docs/BUILT.md`](docs/BUILT.md), "`map-next!`, the one thing a Map could not do". +- ~~**`Map` keys and values.**~~ **Iteration is built** — `flan_map_next` and the `map-next` builtin, exactly the + shape this described. See [`docs/BUILT.md`](docs/BUILT.md), "`map-next`, the one thing a Map could not do". `map-keys`/`map-values` as *prelude functions* stay refused, and the reason is now generics rather than the iterator: a `defn` has to name its types and `(defn map-keys [m {K V}] (Vec K))` has no `K`. The loop is three lines at the call site, where `K` is known. @@ -1941,7 +1941,7 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them. **Generics are deliberately NOT here** — and function values landing has *sharpened* the case rather than made it, which is the useful update. `Vec` and `Map` needed none, being type-erased. Function values needed none. What - needs them is now concrete and small: the prelude's `map!`/`filter`/`reduce`/`sort-by!` are **two copies each**, + needs them is now concrete and small: the prelude's `map-in-place`/`filter`/`reduce`/`sort-by` are **two copies each**, i32 and f32, differing in nothing but the element type; `map-keys`/`map-values` cannot be written at all because a `defn` must name its types and `(defn map-keys [m {K V}] (Vec K))` has no `K`; and a `map` from `[i32]` to `[f32]` would be one copy per ordered pair. A user-written allocator is *not* on this list any more — it wants @@ -2716,7 +2716,7 @@ memcheck sweep (`@valgrind`), whose alarm is looser at 5400s because memcheck is Neither is refused today. The first returns a view of storage the return has just released; the second pushes ptr+len, not the bytes, and a slot reused on the next turn of a loop leaves every element reading as the last number. Copy the bytes for anything that outlives the expression that made them — which is what the prelude's - `append-i64!` and `append-f64!` do, and the reason that shape exists: they copy into a `(Vec u8)`, so a builder + `append-i64` and `append-f64` do, and the reason that shape exists: they copy into a `(Vec u8)`, so a builder holds as many rendered numbers as it likes, and `format-f64` answers a `Vec` rather than a view. `rl/draw-text` is safe for a third reason: the shim's `flan_shim_cstr` copies out of ptr+len before the call. diff --git a/docs/BUILT.md b/docs/BUILT.md index 165a105..ceb9d46 100644 --- a/docs/BUILT.md +++ b/docs/BUILT.md @@ -2658,7 +2658,7 @@ calls per field, which has no channel to hand on. | `(put m k v)` | upsert, `()` | | `(get m k)` | `(Option V)` — absence is `None` | | `(has-key? m k)` | `bool`, copying no value — **an addition; the spec does not name it** | -| `(map-remove! m k)` | `(Option V)` — the value that was there, or `None` | +| `(map-remove m k)` | `(Option V)` — the value that was there, or `None` | | `(len m)` `(reserve m n)` `(clone m)` `(clone m a)` `(free m)` | extended, not duplicated | `has-key?` is **not in `spec-memory.md`** and is an addition, flagged because everything else here is the spec's. @@ -3520,12 +3520,12 @@ Three rules hold across all of it, and they are stated once at the head of the s Odin's `strings.Builder` wraps a `[dynamic]u8`. Here the `(Vec u8)` already **is** that and already has `push`, so the struct would be a move-only wrapper whose only method is the one it wraps. What was actually missing is appending -a *run* of bytes, and `append!` is that. +a *run* of bytes, and `append` is that. It takes a `(Ptr (Vec u8))` and not a `(Vec u8)`, and that is not style: a `Vec` parameter **moves**, so a by-value builder would be consumed by its first append and refused on the second. -`append-i64!` and `append-f64!` are the argument for the whole shape. NEXT.md's "Sharp edges" records that +`append-i64` and `append-f64` are the argument for the whole shape. NEXT.md's "Sharp edges" records that `flan_i64_to_bytes` and its neighbours render into one `static char scratch[64]`, so two formatted numbers cannot be held at once; these copy out of that buffer before returning, so the hazard ends at the call and a builder holds as many numbers as it likes. `strings.flan` puts two integers and a float on one line, which is the case that could not @@ -3539,7 +3539,7 @@ spec-memory.md, "A container of owning elements lives in a region" — and a `(V a region allocator and nowhere else. `split` is unchanged anyway, and now by choice rather than by refusal: an owning `split` would have to allocate one block per field and would only be usable in the tier that can never hand one back, while the slices cost nothing and work everywhere. It follows, as before, that the result dies with whatever the input -pointed at, which is the same contract `trim` and `split-next!` already have. +pointed at, which is the same contract `trim` and `split-next` already have. The rule is `split-on-byte`'s, unchanged: n separators always yield n+1 fields, so an empty input yields one empty field and a trailing separator yields a trailing empty one. That is Odin's allocating `strings.split` and not Odin's @@ -3556,7 +3556,7 @@ gap rather than worked around silently. it. A frame time of 1/60 comes back `0.0166667` and a score past a million `1.23457e+06`. `format-f64` returns a `Vec`, so it inherits neither that nor the shared scratch buffer, and it renders the integer -part and the fraction through that buffer in strict sequence — the discipline `append-i64!` exists to make automatic. +part and the fraction through that buffer in strict sequence — the discipline `append-i64` exists to make automatic. It rounds **half away from zero at the last digit kept**, which is `round-f32`'s rule and the rest of the prelude's. printf rounds the *binary* value to nearest-even at the decimal digit, so `0.125` at two places is `0.13` here and @@ -3592,7 +3592,7 @@ constant-folds a `powf` of two literals and leaves nothing to link. ### What could not be built, and why it is not "no generics" Four things on NEXT.md's list did not land, and the interesting part is that the reason differs in each case. -**Three of the four have since landed** — see "`map-next!`, the one thing a Map could not do", "Function values, with +**Three of the four have since landed** — see "`map-next`, the one thing a Map could not do", "Function values, with no capture" and "A prelude function may call a prelude macro" below — and each was fixed by the thing named here rather than by generics, which is the argument this list was making. The fourth, the path-insensitive dead set, is still open. Kept as written because the diagnoses are what the later lanes worked from, and one of them turned out to @@ -3605,7 +3605,7 @@ be wrong in a way worth being able to see: the prelude *was* reaching the expand - **`map`, `filter`, `reduce` and a comparator sort** are blocked on **function values**, which is sharper than "no generics" and matters because generics alone would not fix it. `Types.Fn` exists; `check.ml` refuses it with "a function type is not implemented yet — milestone 5"; there is nothing in the language to pass. The concrete answer - is the one that shipped: `sort-f32!` and `sort-bytes!` are the second and third sorts in the language, and + is the one that shipped: `sort-f32` and `sort-bytes` are the second and third sorts in the language, and `sum-i32`/`sum-f32` already are `reduce` with the `+` written in. - **The prelude is never macro-expanded**, so a prelude function may not call a prelude macro. `Macro.program` runs over the file being compiled; the prelude reaches the checker through `Check.program`'s prepend. The call resolves @@ -3737,7 +3737,7 @@ backing buffer remains the parameterised allocator that does exist. ### The prelude's four -`map-i32!`/`map-f32!`, `filter-i32`/`filter-f32`, `reduce-i32`/`reduce-f32` and `sort-i32-by!`/`sort-f32-by!`. Two +`map-i32`/`map-f32`, `filter-i32`/`filter-f32`, `reduce-i32`/`reduce-f32` and `sort-i32-by`/`sort-f32-by`. Two rules, both inherited rather than invented: the in-place ones write back into the slice they were handed, because a slice is non-owning and transforming a thing you already own should not allocate; and `filter` allocates and the caller frees, like everything in the building tier. @@ -3746,18 +3746,18 @@ caller frees, like everything in the building tier. pair* of types rather than per type, which is where a per-type family stops being honest. That entry is what is left in `prelude.ml`'s refusal block where `map, filter, reduce, sort-by` used to be, and its reason is generics. -## `map-next!`, the one thing a Map could not do +## `map-next`, the one thing a Map could not do `flan_map_len`, `_get`, `_put`, `_has`, `_clone`, `_reserve` and `_free` was the runtime's entire map surface, and every one of them addresses a *single* entry by hashing it. Nothing walked the block, so a map's keys and its values could not be read out at all — the only item on the second tier's list that was blocked on nothing but a missing function. -`flan_map_next` is that function and `map-next!` is the builtin over it. +`flan_map_next` is that function and `map-next` is the builtin over it. ``` (let [cur (i64 0) k 0 v 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) ...)) ``` @@ -5431,9 +5431,9 @@ to name and nothing to choose between, so the abstract pass could not build the pair would have been worse than refusing: it would hash a string's pointer and a struct's padding. **What closes it is deferral, and what makes deferral safe is the `where` clause.** `put`, `get`, `has-key?`, -`map-remove!`, `reserve` and `clone` — the arms that reach `key_fns` — now check their arguments and then, when the +`map-remove`, `reserve` and `clone` — the arms that reach `key_fns` — now check their arguments and then, when the key is a type variable, return a placeholder of the operation's own type: `Unit` for `put` and `reserve`, `None` for -`get` and for `map-remove!` so the `(Option V)` around either still checks, `false` for `has-key?`, a zeroed map for +`get` and for `map-remove` so the `(Option V)` around either still checks, `false` for `has-key?`, a zeroed map for `clone`. The whole node is thrown away with the rest of the abstract pass, exactly as `println`'s is, and the real one is built when the copy is checked with `$t` concrete. diff --git a/emacs/MANUAL.md b/emacs/MANUAL.md index 4da77ce..742d822 100644 --- a/emacs/MANUAL.md +++ b/emacs/MANUAL.md @@ -721,7 +721,7 @@ program per keystroke. It refreshes at the two moments the answer can have changed: when you connect, and after an evaluation the daemon accepted. The answer covers the compiler's builtins as well as the program's own names, -so `C-c C-v` on `arena-new` or `map-next!` gives you its signature and a line +so `C-c C-v` on `arena-new` or `map-next` gives you its signature and a line about what it does. They are marked `builtin`, and they come after the program's names in a completion list. `M-.` on one refuses rather than jumping: it is written in the compiler, so there is no file to open. diff --git a/emacs/flan-cnr.elc b/emacs/flan-cnr.elc new file mode 100644 index 0000000..e63700a Binary files /dev/null and b/emacs/flan-cnr.elc differ diff --git a/emacs/flan-dape.elc b/emacs/flan-dape.elc new file mode 100644 index 0000000..03c0ef3 Binary files /dev/null and b/emacs/flan-dape.elc differ diff --git a/emacs/flan-inspect.elc b/emacs/flan-inspect.elc new file mode 100644 index 0000000..8633503 Binary files /dev/null and b/emacs/flan-inspect.elc differ diff --git a/emacs/flan-lower.elc b/emacs/flan-lower.elc new file mode 100644 index 0000000..71adf27 Binary files /dev/null and b/emacs/flan-lower.elc differ diff --git a/emacs/flan-mode.elc b/emacs/flan-mode.elc new file mode 100644 index 0000000..ac15f86 Binary files /dev/null and b/emacs/flan-mode.elc differ diff --git a/emacs/flan-repl.elc b/emacs/flan-repl.elc new file mode 100644 index 0000000..6c67c1d Binary files /dev/null and b/emacs/flan-repl.elc differ diff --git a/emacs/flan-watch.elc b/emacs/flan-watch.elc new file mode 100644 index 0000000..3143e5a Binary files /dev/null and b/emacs/flan-watch.elc differ diff --git a/emacs/flan.elc b/emacs/flan.elc new file mode 100644 index 0000000..f70318d Binary files /dev/null and b/emacs/flan.elc differ diff --git a/lib/check.ml b/lib/check.ml index d2818ab..9d7b1df 100644 --- a/lib/check.ml +++ b/lib/check.ml @@ -488,7 +488,7 @@ let pred_holds p (t : Types.t) = admits is a number or an enum, so it is equatable. The table is only sound while that is true — an ordered type with no [=] would make it wrong — so it lives in one place and says so. The gain is real ergonomics: - [{:where (ordered? $t)}] is enough for a [sort!] that also compares, + [{:where (ordered? $t)}] is enough for a [sort] that also compares, rather than two predicates on one line. *) let pred_entails ~declared ~wanted = String.equal declared wanted @@ -846,9 +846,9 @@ let rec generic_ty (t : Types.t) = With [where] there are now two ways out and the message names both: declare the predicate, or take the operation as a function value the way - [sort-by!] does. Declaring it is the one that keeps the call site short, + [sort-by] does. Declaring it is the one that keeps the call site short, which is the whole reason predicates exist — under the no-constraint rule - [(sort! xs)] had to become [(sort-by! xs (fn [a b] (< a b)))] at every call + [(sort xs)] had to become [(sort-by xs (fn [a b] (< a b)))] at every call site in the corpus. *) let unconstrained env loc op ~needs (t : Types.t) = if generic_ty t then @@ -4096,7 +4096,7 @@ and named_call ctx ~want loc name args = [ mk loc oty (Tast.If (cond, some, none)) ]))) | _ -> assert false) - (* (map-remove! m k) -> (Option V): the value that was there, or None when + (* (map-remove m k) -> (Option V): the value that was there, or None when the key was not. The same answer [get] gives, for the same reason — a key that is not in the map is an answer and not a failure — and the value comes back rather than being dropped on the floor, which is what makes @@ -4108,21 +4108,18 @@ and named_call ctx ~want loc name args = the one block the map allocated, and removal moves entries within that block. That is what makes it mean the same thing on a map backed by an arena — or by any allocator that refuses can-free — as on a heap-backed - one. Nothing is freed per entry because nothing was allocated per entry. - - The [!] is the mutation the naming rule asks for ([map-next!], and the - note below on the two suffixes). *) - | "map-remove!" -> + one. Nothing is freed per entry because nothing was allocated per entry. *) + | "map-remove" -> arity loc name 2 args; (match args with | [ target; k ] -> let target = check ctx target in - let kt, vt = map_kv loc "map-remove!" target.Tast.ty in + let kt, vt = map_kv loc "map-remove" target.Tast.ty in let k = check ctx ~want:kt k in (* Deferred exactly as [get] is, and with [None] for the same reason: the abstract pass still has to check whatever the body does with the answer. *) - if deferred_key ctx.env loc "map-remove!" kt then + if deferred_key ctx.env loc "map-remove" kt then expect loc ~want (mk loc (Types.Option vt) Tast.None_) else let hash, eq = key_fns ctx.env loc kt in @@ -4152,7 +4149,7 @@ and named_call ctx ~want loc name args = [ mk loc oty (Tast.If (cond, some, none)) ]))) | _ -> assert false) - (* (map-next! m (addr cur) (addr k) (addr v)) -> bool, and the whole of map + (* (map-next m (addr cur) (addr k) (addr v)) -> bool, and the whole of map iteration. Before it there was no way to read a map's keys or its values at all: every other map operation addresses one entry by hashing it, and nothing walked the block. @@ -4163,7 +4160,7 @@ and named_call ctx ~want loc name args = i64 the caller owns and the loop reads as one: (let [cur 0 k 0 v 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) ...)) It is *not* a generic (map-keys m): a Vec of them needs a signature naming @@ -4173,12 +4170,12 @@ and named_call ctx ~want loc name args = No hash and no equality pair go with it — walking asks nothing about a key — so this is the one map entry point whose signature carries neither, and the sizes are still needed because the runtime is type-erased. *) - | "map-next!" -> + | "map-next" -> arity loc name 4 args; (match args with | [ target; cur; k; v ] -> let target = check ctx target in - let kt, vt = map_kv loc "map-next!" target.Tast.ty in + let kt, vt = map_kv loc "map-next" target.Tast.ty in let cur = check ctx ~want:(Types.Ptr (Types.Int Types.I64)) cur in let k = check ctx ~want:(Types.Ptr kt) k in let v = check ctx ~want:(Types.Ptr vt) v in @@ -4549,9 +4546,9 @@ and named_call ctx ~want loc name args = same trust [declare-c] already extends, written at the one site where somebody had to know the answer anyway. - No marker on the name. A [!] in this language means *mutates* - ([map-next!]) and a [?] means *asks* ([font-valid?]), and this does - neither; [zeroed], the nearest neighbour — a value conjured rather than + No marker on the name. A [?] in this language means *asks* + ([font-valid?]), and this does not ask; [zeroed], the nearest + neighbour — a value conjured rather than derived — carries no marker either. [ptr] is the marker: a (Ptr T) only ever arrives from a [declare-c], so the word already names the C boundary, and a reader who sees it has already been told where the promise comes @@ -4732,7 +4729,7 @@ and named_call ctx ~want loc name args = call site these can be refused at. They are deferred and then always succeed. That is the cheapest possible membership. - The map operations — [put], [get], [has-key?], [map-remove!], [reserve], [clone], + The map operations — [put], [get], [has-key?], [map-remove], [reserve], [clone], through [deferred_key] beside [key_fns] — are the other kind, and they are here on a different argument. They *can* fail at a concrete type, so deferring them does move a refusal. But [{:where (hashable? $t)}] is @@ -4962,7 +4959,7 @@ and generic_call ctx ~want loc name vars pats pret args = generic call site is weaker than at a monomorphic one. A variable already bound by an earlier argument is substituted back into - the parameters still to come, so [(sort-by! (slice ns 0 4) (fn [a b] (< a + the parameters still to come, so [(sort-by (slice ns 0 4) (fn [a b] (< a b)))] works: by the time the [fn] is reached, [(Fn [$t $t] bool)] has become [(Fn [i32 i32] bool)] and the literal has the position it needs to take its types from. Left to right, which is the order Odin's operands @@ -4995,7 +4992,7 @@ and generic_call ctx ~want loc name vars pats pret args = let cret = subst_ty !subst pret in if List.exists generic_ty cparams || generic_ty cret then begin (* One generic function calling another at its *own* variable, seen from - the abstract pass over the caller's body — [sort-by!] calling [swap!] + the abstract pass over the caller's body — [sort-by] calling [swap] at [t]. There is no copy to make yet: [t] is not a type. The node is built so the call still type-checks and is thrown away with the rest of the abstract pass; the real copy is generated when the caller is @@ -5004,7 +5001,7 @@ and generic_call ctx ~want loc name vars pats pret args = But the callee's [where] clause is answerable here, and has to be. The whole promise of the abstract pass is that a generic's refusals arrive at its definition; if the predicate were left to the instantiation, - [(defn f [x $t] () (sort! [x]))] would be accepted at its definition + [(defn f [x $t] () (sort [x]))] would be accepted at its definition and refused at whichever call site first instantiated it — a refusal in code the caller did not write, which is the thing the pass exists to avoid. So the caller has to declare at least what the callee asks for, @@ -5313,12 +5310,12 @@ let builtins : (string * string * string) list = "The value at the key, or None. Nothing signals here — a lookup that \ finds nothing is an answer — and the value comes back as a copy of \ its bytes."); - ("map-remove!", "map-remove! [(Map K V) K] (Option V)", + ("map-remove", "map-remove [(Map K V) K] (Option V)", "Removes the entry and answers the value it held, or None if there was \ none."); - ("map-next!", "map-next! [(Map K V) (Ptr i64) (Ptr K) (Ptr V)] bool", + ("map-next", "map-next [(Map K V) (Ptr i64) (Ptr K) (Ptr V)] bool", "Walks the map one entry per call through a cursor the caller owns, and \ - is the whole of map iteration: (while (map-next! m (addr cur) (addr k) \ + is the whole of map iteration: (while (map-next m (addr cur) (addr k) \ (addr v)) ...)."); ("has-key?", "has-key? [(Map K V) K] bool", "Whether the key is present, copying no value — the form a condition \ @@ -6569,8 +6566,8 @@ let instantiations env gname = | Some l -> List.rev_map (fun (_, _, sym) -> sym) !l (* The generic a symbol came from, and the types it was asked for — [None] for - an ordinary function. What a refusal about [sort!-i32] needs in order to - say which line the programmer should look at, since [sort!-i32] appears + an ordinary function. What a refusal about [sort-i32] needs in order to + say which line the programmer should look at, since [sort-i32] appears nowhere in the source. *) let instantiation_origin env sym = Hashtbl.fold diff --git a/lib/js.ml b/lib/js.ml index d828e63..c2c8922 100644 --- a/lib/js.ml +++ b/lib/js.ml @@ -170,8 +170,8 @@ let at loc fmt = (* ── Names ────────────────────────────────────────────────────────── - A Flan name is not a JS identifier: [rand-u32], [bytes=?], [append!] and - [fn/sort-bytes!/0] are all ordinary. The rule below is injective, which is + A Flan name is not a JS identifier: [rand-u32], [bytes=?], [append] and + [fn/sort-bytes/0] are all ordinary. The rule below is injective, which is what matters — two Flan names must never land on one JS name — and readable second: [-] is the common case and becomes [_], so [rand-u32] reads as [rand_u32], and an underscore that was actually written becomes [$_] so diff --git a/lib/parse.ml b/lib/parse.ml index 2569ec6..b1007c8 100644 --- a/lib/parse.ml +++ b/lib/parse.ml @@ -112,7 +112,7 @@ let rec fields (f : Form.t) (items : Form.t list) : Ast.field list = (Form.to_string odd) (* ── The constraint map at the head of a defn body ────────────────────── - [(defn sort! [s [$t]] () {:where (ordered? $t)} body ...)]. Clojure's + [(defn sort [s [$t]] () {:where (ordered? $t)} body ...)]. Clojure's [{:pre [...] :post [...]}] is the precedent and the reason it is a map rather than a bare keyword: it leaves room for further keys without new syntax. diff --git a/lib/prelude.ml b/lib/prelude.ml index ea4c42b..95b5759 100644 --- a/lib/prelude.ml +++ b/lib/prelude.ml @@ -219,7 +219,7 @@ let source = {flan| ;; ;; What used to be a copy per element type. A [$t] binds a type variable in ;; the signature and every call site instantiates the body at the types it -;; passes, so [(sort! xs)] over a [i32] and over a [f32] are two emitted +;; passes, so [(sort xs)] over a [i32] and over a [f32] are two emitted ;; bodies from one written one. ;; ;; **Two things in the signatures are not decoration.** @@ -243,22 +243,22 @@ let source = {flan| ;; widen their element into [i64] and [f64]; "the wider type $t accumulates ;; into" is a type-level function, which is a constraint system of a different ;; kind, and a generic [sum] that took its accumulator and its [+] would just -;; be [reduce]. [append-i64!] and [append-f64!] are two different primitives. -;; [sort-bytes!] needs [bytes] rather than [<] — a [[u8]] is not [ordered?] -;; and cannot be — so it is [sort-by!] with the comparison written in, and it +;; be [reduce]. [append-i64] and [append-f64] are two different primitives. +;; [sort-bytes] needs [bytes] rather than [<] — a [[u8]] is not [ordered?] +;; and cannot be — so it is [sort-by] with the comparison written in, and it ;; keeps its name because the stability contract in its comment is worth ;; keeping attached to something. -(defn swap! [s [$t] i i32 j i32] () +(defn swap [s [$t] i i32 j i32] () (let [t (at s i)] (set (at s i) (at s j)) (set (at s j) t))) -(defn reverse! [s [$t]] () +(defn reverse [s [$t]] () (let [i 0 j (- (len s) 1)] (while (< i j) - (swap! s i j) + (swap s i j) (set i (+ i 1)) (set j (- j 1))))) @@ -274,7 +274,7 @@ let source = {flan| ;; qsort with a naive comparator does too, and the only fix is not to have ;; NaNs in the array — there is no ordering of the reals a NaN sits anywhere ;; in. -(defn sort! [s [$t]] () +(defn sort [s [$t]] () {:where (ordered? $t)} (let [i 1] (while (< i (len s)) @@ -282,7 +282,7 @@ let source = {flan| ;; `and` short-circuits, which is load-bearing: at j = 0 the left test ;; fails and (at s -1) is never evaluated, so this does not trap. (while (and (> j 0) (> (at s (- j 1)) (at s j))) - (swap! s (- j 1) j) + (swap s (- j 1) j) (set j (- j 1)))) (set i (+ i 1))))) @@ -291,7 +291,7 @@ let source = {flan| ;; (fn [a b] (< a b)) is ascending and reversing it is descending — and a ;; caller wanting a key rather than an order writes the comparison. ;; -;; It is stable exactly as sort! is: the loop stops the moment before? says +;; It is stable exactly as sort is: the loop stops the moment before? says ;; no, so equal elements never swap past each other. A before? that is not a ;; strict weak ordering — one answering true for both (a b) and (b a) — is the ;; caller's mistake and shows up as an order, not as a loop: the inner while @@ -301,12 +301,12 @@ let source = {flan| ;; comparison it is given. It is the shape every generic had to take before ;; predicates existed, and it stays because passing a comparison is a real ;; thing to want and not only a workaround. -(defn sort-by! [s [$t] before? (Fn [$t $t] bool)] () +(defn sort-by [s [$t] before? (Fn [$t $t] bool)] () (let [i 1] (while (< i (len s)) (let [j i] (while (and (> j 0) (before? (at s j) (at s (- j 1)))) - (swap! s (- j 1) j) + (swap s (- j 1) j) (set j (- j 1)))) (set i (+ i 1))))) @@ -359,7 +359,7 @@ let source = {flan| ;; ;; **Neither takes a {:where}, and that is a decision rather than an ;; oversight.** A predicate buys an *operation* on the variable — [ordered?] -;; is what lets sort! write `<` — and these perform no operation on their +;; is what lets sort write `<` — and these perform no operation on their ;; payload at all: they move it out of the Option, or they look at the tag and ;; never touch the payload. That is the one move [ident] in ;; test/programs/generics.flan makes, which needs nothing declared, so these @@ -377,7 +377,7 @@ let source = {flan| ;; ordinary path. Whether an empty Option is an error is the ;; *caller's* question, and the caller has handler-bind if ;; the answer is yes. -;; a lazy or-else Would need a (Fn [] $t) — sort-by!'s shape, available the +;; a lazy or-else Would need a (Fn [] $t) — sort-by's shape, available the ;; day something wants it. A macro would get laziness for ;; free and need no generics, and costs more than it buys ;; here: a prelude macro drops every prelude defn that @@ -411,12 +411,12 @@ let source = {flan| (defn some? [o (Option $t)] bool (match o (Some _v) true None false)) -;; map! writes back into the slice it was handed, for the same reason sort! +;; map-in-place writes back into the slice it was handed, for the same reason sort ;; does — a slice is non-owning, and transforming a thing you already own ;; should not allocate. A map that produces a *different* element type is not ;; here: it is two type variables and a second signature, and nothing has ;; wanted it. -(defn map! [s [$t] f (Fn [$t] $t)] () +(defn map-in-place [s [$t] f (Fn [$t] $t)] () (dotimes [i (len s)] (set (at s i) (f (at s i))))) @@ -1248,7 +1248,7 @@ let source = {flan| ;; replacement character and reports success; the caller then finds three ;; bytes of U+FFFD in its buffer and no indication that it asked for something ;; else. Nothing is written at all when this answers None. -(defn encode-rune! [dst [u8] code i32] (Option i32) +(defn encode-rune [dst [u8] code i32] (Option i32) (match (rune-size code) None None (Some w) @@ -1295,7 +1295,7 @@ let source = {flan| (defn split-on-byte [s [u8] sep u8] Split (Split {.rest s .sep sep .more true})) -(defn split-next! [it (Ptr Split)] (Option [u8]) +(defn split-next [it (Ptr Split)] (Option [u8]) (when (not (.more it)) (return None)) (match (index-of (.rest it) (.sep it)) @@ -1320,7 +1320,7 @@ let source = {flan| ;; saying why rather than shipping it. A string ;; literal is emitted `private unnamed_addr constant` (emit.ml), so (bytes ;; "Hello") is a [u8] pointing straight into read-only memory. An in-place -;; lower-ascii! type checks against that slice, and what happens next depends +;; lower-ascii type checks against that slice, and what happens next depends ;; on the optimiser — which is the worst of the available answers. Measured, ;; with (set (at (bytes "Hi") 0) \h): ;; @@ -1384,20 +1384,20 @@ let source = {flan| (return (< (at a i) (at b i))))) (< (len a) (len b)))) -;; sort-by! with the comparison written in, over the same in-place contract: +;; sort-by with the comparison written in, over the same in-place contract: ;; the *slices* move, never the bytes they point at, so this sorts a [[u8]] of ;; fields borrowed from one buffer without touching the buffer. Stable, and ;; here that is observable — two equal fields are two distinct slices of ;; different parts of the input, and a caller can see which one came first. ;; -;; It keeps a name of its own rather than collapsing into sort!, and the +;; It keeps a name of its own rather than collapsing into sort, and the ;; reason is the point of the predicates: a [u8] is not ordered? and cannot ;; be, because < is defined on machine numbers and comparing two slices ;; lexicographically is a loop and not an instruction. bytes is that loop. ;; So this is the shape a generic takes when the operation it needs is not a ;; primitive: pass it in. -(defn sort-bytes! [s [[u8]]] () - (sort-by! s (fn [a b] (bytes a b)))) +(defn sort-bytes [s [[u8]]] () + (sort-by s (fn [a b] (bytes a b)))) ;; ── Building bytes, which is the tier that needed an allocator ──────── ;; @@ -1433,9 +1433,9 @@ let source = {flan| ;; bytes rather than one, and that is this. ;; ;; It takes a (Ptr (Vec u8)) and not a (Vec u8), and the difference is not -;; style: a Vec parameter *moves*, so (append! b s) taking one by value would +;; style: a Vec parameter *moves*, so (append b s) taking one by value would ;; consume the caller's builder on the first call and refuse the second. -(defn append! [b (Ptr (Vec u8)) s [u8]] () +(defn append [b (Ptr (Vec u8)) s [u8]] () (dotimes [i (len s)] (push (deref b) (at s i)))) @@ -1446,11 +1446,11 @@ let source = {flan| ;; views of the same bytes — the second call overwrote the first. These copy ;; out of that buffer before returning, so the hazard ends at the call: a ;; builder can hold as many numbers as it likes. -(defn append-i64! [b (Ptr (Vec u8)) n i64] () - (append! b (i64->bytes n))) +(defn append-i64 [b (Ptr (Vec u8)) n i64] () + (append b (i64->bytes n))) -(defn append-f64! [b (Ptr (Vec u8)) x f64] () - (append! b (f64->bytes x))) +(defn append-f64 [b (Ptr (Vec u8)) x f64] () + (append b (f64->bytes x))) ;; concat and join. Both take a slice of slices, which is the shape a caller ;; already has: an array literal of them, [(bytes "a") (bytes b)], slices to a @@ -1462,7 +1462,7 @@ let source = {flan| (defn concat [parts [[u8]]] (Vec u8) (let [b (vec-new u8)] (dotimes [i (len parts)] - (append! (addr b) (at parts i))) + (append (addr b) (at parts i))) b)) ;; n parts yield n-1 separators, and the empty slice of parts yields the empty @@ -1473,14 +1473,14 @@ let source = {flan| (let [b (vec-new u8)] (dotimes [i (len parts)] (when (> i 0) - (append! (addr b) sep)) - (append! (addr b) (at parts i))) + (append (addr b) sep)) + (append (addr b) (at parts i))) b)) (defn repeat-bytes [s [u8] n i32] (Vec u8) (let [b (vec-new u8)] (dotimes [i n] - (append! (addr b) s)) + (append (addr b) s)) b)) ;; The allocating halves of the ASCII case pair. The note above lower-ascii @@ -1517,17 +1517,17 @@ let source = {flan| (let [b (vec-new u8) i 0] (if (= (len from) 0) - (append! (addr b) s) + (append (addr b) s) (while (< i (len s)) (match (index-of-bytes (slice s i (len s)) from) (Some k) (do - (append! (addr b) (slice s i (+ i k))) - (append! (addr b) to) + (append (addr b) (slice s i (+ i k))) + (append (addr b) to) (set i (+ i k (len from)))) None (do - (append! (addr b) (slice s i (len s))) + (append (addr b) (slice s i (len s))) (set i (len s)))))) b)) @@ -1554,7 +1554,7 @@ let source = {flan| it (split-on-byte s sep) going true] (while going - (match (split-next! (addr it)) + (match (split-next (addr it)) (Some f) (push v f) None (set going false))) v)) @@ -1572,7 +1572,7 @@ let source = {flan| ;; This returns a Vec, so neither problem is inherited. It uses i64->bytes ;; twice and the two calls are strictly sequential — the integer part is copied ;; into the Vec before the fraction is rendered — which is the discipline the -;; shared buffer requires and the one append-i64! exists to make automatic. +;; shared buffer requires and the one append-i64 exists to make automatic. ;; ;; Half away from zero, the same rule round-f32 follows, applied at the last ;; digit kept. That is not bit-for-bit printf: printf rounds the *binary* value @@ -1610,16 +1610,16 @@ let source = {flan| p (clamp prec 0 9)] (cond (not (= x x)) - (append! (addr b) (bytes "nan")) + (append (addr b) (bytes "nan")) (and (= x (* x 2.0)) (!= x 0.0)) - (append! (addr b) (bytes (if (< x 0.0) "-inf" "inf"))) + (append (addr b) (bytes (if (< x 0.0) "-inf" "inf"))) :else (let [neg (< x 0.0) m (if neg (- 0.0 x) x)] (if (>= m 9.0e18) - (append! (addr b) (f64->bytes x)) + (append (addr b) (f64->bytes x)) (let [scale (i64 1)] (dotimes [i p] (set scale (* scale 10))) @@ -1642,7 +1642,7 @@ let source = {flan| ;; i64->bytes of 0 has no sign to carry. (when neg (push b \-)) - (append-i64! (addr b) ip) + (append-i64 (addr b) ip) (when (> p 0) (push b \.) ;; Left-padded with zeros to exactly p digits. fr is under @@ -1651,7 +1651,7 @@ let source = {flan| (let [d (i64->bytes fr)] (dotimes [i (- p (len d))] (push b \0)) - (append! (addr b) d)))))))) + (append (addr b) d)))))))) b)) ;; ── Still refused, and what the reason is now ───────────────────────── @@ -1676,8 +1676,8 @@ let source = {flan| ;; allocation one. format-f64 above is the piece of it ;; that was actually wanted, and `print`/`println` are ;; already the structural walk over any one value. -;; map that changes the Generics, and only that. map!, filter, reduce and -;; element type sort-by! landed the day function values did — see +;; map that changes the Generics, and only that. map-in-place, filter, reduce and +;; element type sort-by landed the day function values did — see ;; "The ones that take a function" above — at i32 and ;; f32, the two element types the rest of that family ;; covers. A map from [i32] to [f32] is the one shape @@ -1686,7 +1686,7 @@ let source = {flan| ;; which is where a per-type family stops being honest. ;; map-keys, map-values Generics — and the reason changed, which is the ;; point of naming them separately. It used to be the -;; missing Map iterator; `map-next!` is that iterator +;; missing Map iterator; `map-next` is that iterator ;; and walking a map is expressible now. What a defn ;; still cannot say is (defn map-keys [m {K V}] (Vec K)): ;; a prelude function has to name its types, and there @@ -1699,7 +1699,7 @@ let source = {flan| ;; already has push, so the struct would be a move-only ;; wrapper whose only method is the one it wraps. What ;; was missing was appending a run of bytes, and -;; `append!` above is that. +;; `append` above is that. ;; ── Files: embedding, slurp and barf ────────────────────────────────── ;; ;; One entry per file in an (embed-dir "...") — Odin's Load_Directory_File diff --git a/lib/session.ml b/lib/session.ml index abff502..230158c 100644 --- a/lib/session.ml +++ b/lib/session.ml @@ -206,11 +206,11 @@ let compatible ?(origin = fun _ -> None) ~loc (old_ : Tast.program) plan.org, Hot reload, and open decision #6. *) if not same then (* ── When the name is not one the programmer wrote ────────────── - A generic's instantiations are named [sort!-i32], [sort!-f32] + A generic's instantiations are named [sort-i32], [sort-f32] and so on, and the mangling carries only the *type variables* — so editing the generic's other parameters changes every copy's signature at once, under the same names. The refusal then - arrives about [sort!-i32], which appears nowhere in the file + arrives about [sort-i32], which appears nowhere in the file being edited, for a reason invisible at the edited line. So the refusal says where the name came from: which generic, at diff --git a/plan.org b/plan.org index 84c6f41..d94df05 100644 --- a/plan.org +++ b/plan.org @@ -230,7 +230,7 @@ and on a managed ~class~ instance. An ordinary ~struct~ never carries one. return type, or nested as ~[$t]~ or ~(Vec $t)~ — and bare ~t~ wherever a type's *name* is an argument in expression position: ~(vec-new t)~, ~(map-new t i32)~, ~(pool-new t)~, and the cast ~(t x)~. This is what makes - ~map!~/~filter~/~reduce~ and the monomorphic containers work; it collapsed the + ~map-in-place~/~filter~/~reduce~ and the monomorphic containers work; it collapsed the prelude's per-type families into one function each. A generic body is checked *abstractly*, with nothing substituted, so ~=~, ~<~, ~+~ and ~hash~ over an unconstrained variable are rejected at the definition @@ -243,7 +243,7 @@ and on a managed ~class~ instance. An ordinary ~struct~ never carries one. the head of the body — ~{:where (ordered? $t)}~, or a vector for more than one, ~{:where [(copyable? $t) (copyable? $u)]}~ — on the precedent of Clojure's ~{:pre ... :post ...}~, and because a bare ~{}~ in expression position is - already refused so nothing else it could be. ~sort!~ declares ~ordered?~ of its + already refused so nothing else it could be. ~sort~ declares ~ordered?~ of its variable, the abstract pass then allows ~<~ in the body, and each instantiation checks the concrete type satisfies the predicate and refuses the call site if it does not. There are *five* predicates — ~ordered?~, ~equal?~, ~hashable?~, diff --git a/runtime/flan_rt.c b/runtime/flan_rt.c index d1a7e5d..53b4bfc 100644 --- a/runtime/flan_rt.c +++ b/runtime/flan_rt.c @@ -975,7 +975,7 @@ struct flan_allocator { * * Every size this file computes is a signed 64-bit count of bytes, and every * one of them is a product or a sum of numbers a Flan program chose: a - * capacity from (vec-reserve!), an element size from the checker. Signed + * capacity from (reserve), an element size from the checker. Signed * overflow is undefined, and the defined-in-practice outcome is worse than * the undefined one — a product that wraps to a small positive allocates a * block that fits while the container records the unwrapped capacity, and the @@ -1566,7 +1566,7 @@ static int8_t flan_vec_grow(flan_vec *v, int64_t want, int64_t size, if (cap > (int64_t)1 << 40) { cap = want; break; } cap *= 2; } - /* [want] arrives from (vec-reserve!) unfiltered, and the doubling loop above + /* [want] arrives from (reserve) unfiltered, and the doubling loop above * hands a want past 1<<40 straight through as the capacity, so this product * is the one the program picked times the one the checker did. See the note * on flan_mul_bytes. */ diff --git a/spec-memory.md b/spec-memory.md index e3dfa8d..5345170 100644 --- a/spec-memory.md +++ b/spec-memory.md @@ -263,7 +263,7 @@ move concept it opted out of.) plan.org's Types section has the full account. ``` -(defn sort! [s [$t]] () +(defn sort [s [$t]] () {:where (ordered? $t)} ...) ``` diff --git a/test/programs/algorithms.flan b/test/programs/algorithms.flan index 847a055..052cdc9 100644 --- a/test/programs/algorithms.flan +++ b/test/programs/algorithms.flan @@ -1,6 +1,6 @@ ;;;; The slice family at its second and third element types. ;;;; -;;;; sort! was the only sort in the language. These are the other two, and +;;;; sort was the only sort in the language. These are the other two, and ;;;; they are copies rather than an abstraction: map, filter, reduce and a sort ;;;; taking a comparator all need a *function value*, which check.ml refuses ;;;; with "a function type is not implemented yet -- milestone 5". So the @@ -22,41 +22,41 @@ (defn main [] i32 ;; Every float literal is cast. A literal defaults to f64 and an array ;; literal has no context to say otherwise -- a let has no type annotation -- - ;; so [3.5 -1.0] is an [f64] and (sort!) refuses it by type. The cast is + ;; so [3.5 -1.0] is an [f64] and (sort) refuses it by type. The cast is ;; the only spelling available today. ;; - ;; sort!: duplicates, negatives, a zero and an odd length, which is the + ;; sort: duplicates, negatives, a zero and an odd length, which is the ;; input shape the i32 sort is tested on for the same reasons. (let [xs [(f32 3.5) (f32 -1.0) (f32 0.0) (f32 3.5) (f32 -2.25) (f32 10.0) (f32 0.5)]] - (sort! (slice xs 0 7)) + (sort (slice xs 0 7)) (show-f32 (slice xs 0 7))) ; -2.25 -1 0 0.5 3.5 3.5 10 ;; In place and ptr+len: sorting a subslice leaves its neighbours alone. That ;; is the whole content of the in-place claim, and a version that copied ;; would pass every test above and fail this one. (let [xs [(f32 9.0) (f32 4.0) (f32 3.0) (f32 2.0) (f32 1.0) (f32 9.0)]] - (sort! (slice xs 1 5)) + (sort (slice xs 1 5)) (show-f32 (slice xs 0 6))) ; 9 1 2 3 4 9 ;; Already sorted, reverse sorted, and a single element -- the three inputs ;; where an insertion loop with the comparison the wrong way round still ;; looks plausible. (let [xs [(f32 1.0) (f32 2.0) (f32 3.0)]] - (sort! (slice xs 0 3)) + (sort (slice xs 0 3)) (show-f32 (slice xs 0 3))) ; 1 2 3 (let [xs [(f32 3.0) (f32 2.0) (f32 1.0)]] - (sort! (slice xs 0 3)) + (sort (slice xs 0 3)) (show-f32 (slice xs 0 3))) ; 1 2 3 (let [xs [(f32 7.0)]] - (sort! (slice xs 0 1)) + (sort (slice xs 0 1)) (show-f32 (slice xs 0 1))) ; 7 ;; The empty slice must not read (at s -1). (let [xs [(f32 7.0)]] - (sort! (slice xs 0 0)) + (sort (slice xs 0 0)) (show-f32 (slice xs 0 0))) ; (let [xs [(f32 1.0) (f32 2.0) (f32 3.0) (f32 4.0)]] - (reverse! (slice xs 0 4)) + (reverse (slice xs 0 4)) (show-f32 (slice xs 0 4))) ; 4 3 2 1 ;; min, max and sum. The empty slice is None for the first two -- there is no @@ -102,17 +102,17 @@ (print (bytes (slice hi 0 1) (slice lo 0 1))) ; false (println "")) - ;; sort-bytes! over the fields split out of one buffer. The slices move and + ;; sort-bytes over the fields split out of one buffer. The slices move and ;; the bytes never do, so this sorts a borrowed view of a string literal -- ;; which an in-place byte sort could not, since a literal lives in .rodata. (let [f (split (bytes "pear,apple,Fig,apple,banana") \,)] - (sort-bytes! (as-slice f)) + (sort-bytes (as-slice f)) (show-fields (as-slice f)) ; Fig apple apple banana pear (free f)) ;; And the round trip the whole second tier is for: split, sort, join. (let [f (split (bytes "delta,alpha,charlie,bravo") \,)] - (sort-bytes! (as-slice f)) + (sort-bytes (as-slice f)) (let [j (join (as-slice f) (bytes " < "))] (println (string (as-slice j))) ; alpha < bravo < charlie < delta (free j)) diff --git a/test/programs/arena-edn.flan b/test/programs/arena-edn.flan index 556ffd7..7da47d0 100644 --- a/test/programs/arena-edn.flan +++ b/test/programs/arena-edn.flan @@ -60,7 +60,7 @@ (dotimes [i (len items)] (set n (+ n (count-leaves (at items i))))) n) - ;; map-next! fills an out-parameter with a copy of the value's bytes, + ;; map-next fills an out-parameter with a copy of the value's bytes, ;; which for a Value holding a container is a second header over the same ;; block. In a region that is an alias and not a second owner — nothing ;; here owns anything, the arena does — so walking a map is the ordinary @@ -70,7 +70,7 @@ cur (i64 0) k "" v edn/Value.Nil] - (while (map-next! entries (addr cur) (addr k) (addr v)) + (while (map-next entries (addr cur) (addr k) (addr v)) (set n (+ n (count-leaves v)))) n) _ 1)) diff --git a/test/programs/edn-read.flan b/test/programs/edn-read.flan index 358fd0e..d6b18e3 100644 --- a/test/programs/edn-read.flan +++ b/test/programs/edn-read.flan @@ -86,10 +86,10 @@ ;; A document in a buffer this program owns and can write to. (bytes "literal") ;; is not that — a literal is constant data behind a writable-looking slice — -;; so the source is built with append! and the write goes through as-slice. +;; so the source is built with append and the write goes through as-slice. (defn survives-its-buffer [] () (let [buf (vec-new u8)] - (append! (addr buf) (bytes "{:name \"level-1\" :xs [1 2]}")) + (append (addr buf) (bytes "{:name \"level-1\" :xs [1 2]}")) (let [src (as-slice buf) v (edn/read src)] (dotimes [i (len src)] diff --git a/test/programs/fn-values.flan b/test/programs/fn-values.flan index c2d872a..7875c5b 100644 --- a/test/programs/fn-values.flan +++ b/test/programs/fn-values.flan @@ -10,7 +10,7 @@ ;; The shape map/filter/reduce want: the function arrives as a parameter, is ;; called, and is never stored. -(defn each! [xs [i32] f (Fn [i32] i32)] () +(defn each [xs [i32] f (Fn [i32] i32)] () (dotimes [i (len xs)] (set (at xs i) (f (at xs i))))) @@ -23,11 +23,11 @@ ;; A comparator, which is the other half of what was blocked: a sort that is ;; told the order rather than having it written in. Insertion sort, because the ;; point here is the parameter and not the algorithm. -(defn insertion-by! [xs [i32] before? (Fn [i32 i32] bool)] () +(defn insertion-by [xs [i32] before? (Fn [i32 i32] bool)] () (dotimes [i (len xs)] (let [j i] (while (and (> j 0) (before? (at xs j) (at xs (- j 1)))) - (swap! xs j (- j 1)) + (swap xs j (- j 1)) (set j (- j 1)))))) (defn ascending [a i32 b i32] bool (< a b)) @@ -62,7 +62,7 @@ (defn main [] i32 (let [xs [1 2 3 4]] ;; A name in value position, passed down. - (each! (slice xs 0 4) double) + (each (slice xs 0 4) double) (print (at xs 0)) (print " ") (print (at xs 3)) (println "") ;; 2 + 4 + 6 + 8 negated (println (fold (slice xs 0 4) negate)) @@ -75,12 +75,12 @@ ;; A comparator, and the same slice sorted both ways. (let [ys [3 1 4 1 5 9 2 6] s (slice ys 0 8)] - (insertion-by! s ascending) + (insertion-by s ascending) (print (at s 0)) (print " ") (print (at s 7)) (println "") - (insertion-by! s descending) + (insertion-by s descending) (print (at s 0)) (print " ") (print (at s 7)) (println "") ;; A returned function value, and a computed head calling it. - (insertion-by! s (pick true)) + (insertion-by s (pick true)) (print (at s 0)) (println "") (println ((pick false) 1 2))) diff --git a/test/programs/format.flan b/test/programs/format.flan index 4fac218..5005c41 100644 --- a/test/programs/format.flan +++ b/test/programs/format.flan @@ -87,13 +87,13 @@ ;; needs the integer part copied out before the fraction is rendered, because ;; both come through the runtime's one shared scratch buffer. (let [b (vec-new u8)] - (append! (addr b) (bytes "fps ")) + (append (addr b) (bytes "fps ")) (let [f (format-f64 59.94 1)] - (append! (addr b) (as-slice f)) + (append (addr b) (as-slice f)) (free f)) - (append! (addr b) (bytes " / frame ")) + (append (addr b) (bytes " / frame ")) (let [f (format-f64 0.0166667 4)] - (append! (addr b) (as-slice f)) + (append (addr b) (as-slice f)) (free f)) (println (string (as-slice b))) ; fps 59.9 / frame 0.0167 (free b)) diff --git a/test/programs/generics.flan b/test/programs/generics.flan index 7fb2f1c..87ed185 100644 --- a/test/programs/generics.flan +++ b/test/programs/generics.flan @@ -25,11 +25,11 @@ (defn first-or [s [$t] d $t] $t (if (= (len s) 0) d (at s 0))) -;; A generic calling a generic at its own variable: the copy of [swap!] is -;; generated when [rotate!] is instantiated and not before. -(defn rotate! [s [$t]] () +;; A generic calling a generic at its own variable: the copy of [swap] is +;; generated when [rotate] is instantiated and not before. +(defn rotate [s [$t]] () (dotimes [i (- (len s) 1)] - (swap! s i (+ i 1)))) + (swap s i (+ i 1)))) ;; numeric? admits + - * / %. (defn twice [x $t] $t @@ -121,7 +121,7 @@ fs [2.5 0.5 1.5]] (println (first-or (slice ns 0 4) -1)) (println (first-or (slice ns 0 0) -1)) - (rotate! (slice ns 0 4)) + (rotate (slice ns 0 4)) (println (at ns 3)) (println (twice 21)) @@ -136,13 +136,13 @@ (show "text") ;; The collapsed prelude family, at both element types. - (sort! (slice ns 0 4)) + (sort (slice ns 0 4)) (println (at ns 0)) - (sort-by! (slice fs 0 3) (fn [a b] (> a b))) + (sort-by (slice fs 0 3) (fn [a b] (> a b))) (println (at fs 0)) - (reverse! (slice ns 0 4)) + (reverse (slice ns 0 4)) (println (at ns 0)) - (map! (slice ns 0 4) (fn [x] (* x 2))) + (map-in-place (slice ns 0 4) (fn [x] (* x 2))) (println (reduce (slice ns 0 4) 0 (fn [a b] (+ a b)))) (match (min-of (slice ns 0 4)) (Some m) (println m) _ (println -1)) (match (max-of (slice fs 0 3)) (Some m) (println m) _ (println -1.0)) diff --git a/test/programs/higher-order.flan b/test/programs/higher-order.flan index e0cea10..6d64cd2 100644 --- a/test/programs/higher-order.flan +++ b/test/programs/higher-order.flan @@ -1,4 +1,4 @@ -;; The prelude's function-taking family: map!, filter, reduce and a comparator +;; The prelude's function-taking family: map-in-place, filter, reduce and a comparator ;; sort. These were the four the second tier could not write, and they arrived ;; the day function values did — so what this checks is that they are ordinary ;; prelude functions, called the ordinary way, with the function passed by @@ -12,10 +12,10 @@ (defn big? [x f32] bool (> x 1.0)) (defn main [] i32 - ;; map! writes back into the slice it was handed. + ;; map-in-place writes back into the slice it was handed. (let [xs [1 2 3 4] s (slice xs 0 4)] - (map! s triple) + (map-in-place s triple) (print (at s 0)) (print " ") (print (at s 3)) (println "") ;; reduce, with the accumulator first in the step. The prelude's own @@ -30,21 +30,21 @@ (free v)) ;; A comparator sort, both directions off the same slice. - (sort-by! s longer-first) + (sort-by s longer-first) (print (at s 0)) (print " ") (print (at s 3)) (println "") - (sort-by! s (fn [a b] (< a b))) + (sort-by s (fn [a b] (< a b))) (print (at s 0)) (print " ") (print (at s 3)) (println "")) ;; The f32 half of the family, which is the same code at the other element ;; type — the copy that generics would remove. (let [ys [(f32 4.0) (f32 1.0) (f32 8.0) (f32 2.0)] t (slice ys 0 4)] - (map! t halve) + (map-in-place t halve) (print (at t 0)) (print " ") (print (at t 2)) (println "") (print (reduce t 0.0 (fn [a b] (+ a b)))) (println "") (let [w (filter t big?)] (print (len w)) (println "") (free w)) - (sort-by! t (fn [a b] (> a b))) + (sort-by t (fn [a b] (> a b))) (print (at t 0)) (print " ") (print (at t 3)) (println "")) 0) diff --git a/test/programs/json.flan b/test/programs/json.flan index 8b51ac6..b171568 100644 --- a/test/programs/json.flan +++ b/test/programs/json.flan @@ -208,7 +208,7 @@ (dotimes [i (len items)] (set n (+ n (count-leaves (at items i))))) n) - ;; map-next! fills an out-parameter with a copy of the value's bytes, which + ;; map-next fills an out-parameter with a copy of the value's bytes, which ;; for a Value holding a container is a second header over the same block. ;; In a region that is an alias and not a second owner, so walking a map is ;; the ordinary iteration and needs no accessor of its own. @@ -217,7 +217,7 @@ cur (i64 0) k "" e Value.Null] - (while (map-next! entries (addr cur) (addr k) (addr e)) + (while (map-next entries (addr cur) (addr k) (addr e)) (set n (+ n (count-leaves e)))) n) _ 1)) @@ -347,7 +347,7 @@ ;; Strings: the escape grammar, and the two surrogate halves. A lone ;; surrogate is refused because rune-size answers None for the whole - ;; D800-DFFF block, so encode-rune! would write nothing and the character + ;; D800-DFFF block, so encode-rune would write nothing and the character ;; would vanish — the refusal is forced by the prelude rather than chosen. (refusal "\"a\\vb\"") ; \v is JSON5's (refusal "\"a\\x41b\"") ; so is \x @@ -405,7 +405,7 @@ ;; two lifetimes have to be separable for the scribble below to mean ;; anything. (let [buf (vec-new u8)] - (append! (addr buf) (bytes doc)) + (append (addr buf) (bytes doc)) (with-allocator frame (let [v (read-doc (as-slice buf))] (println (describe v)) ; object diff --git a/test/programs/map-iter.flan b/test/programs/map-iter.flan index 9b69829..913b18e 100644 --- a/test/programs/map-iter.flan +++ b/test/programs/map-iter.flan @@ -19,7 +19,7 @@ keys 0 vals 0 n 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set keys (+ keys k)) (set vals (+ vals v)) (set n (+ n 1))) @@ -35,12 +35,12 @@ k 0 v 0 n 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (print "never allocated: ") (print n) (println "") (reserve m 64) (set cur (i64 0)) - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (print "allocated and empty: ") (print n) (println "") (free m))) @@ -52,9 +52,9 @@ (put m 5 50) (put m 6 60) (let [cur (i64 0) k 0 v 0 n 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1))) (print "spent: ") (print n) (println "")) (free m))) @@ -75,7 +75,7 @@ chars 0 xs 0 ys 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set chars (+ chars (len k))) (set xs (+ xs (.x v))) (set ys (+ ys (.y v)))) @@ -94,7 +94,7 @@ v (i64 0) n 0 doubled 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set n (+ n 1)) (when (= v (* k 2)) (set doubled (+ doubled 1)))) (print n) (print " ") (print doubled) (print " ") (print (len m)) (println "")) diff --git a/test/programs/map-remove.flan b/test/programs/map-remove.flan index 85223c8..20f2e78 100644 --- a/test/programs/map-remove.flan +++ b/test/programs/map-remove.flan @@ -1,4 +1,4 @@ -;;;; (map-remove! m k) — the operation a Map has been missing. +;;;; (map-remove m k) — the operation a Map has been missing. ;;;; ;;;; Removal is the one map operation that can break the *other* ones: Robin ;;;; Hood lookups stop at the first empty slot, so a hole punched in the middle @@ -19,12 +19,12 @@ (let [m (map-new i32 i64)] (put m 1 100) (put m 2 200) - (match (map-remove! m 1) + (match (map-remove m 1) (Some v) (do (print v) (println "")) ; 100 None (println "missing")) (print (len m)) (println "") ; 1 (print (has-key? m 1)) (println "") ; false - (match (map-remove! m 1) (Some v) (do (print v) (println "")) None (println "gone")) + (match (map-remove m 1) (Some v) (do (print v) (println "")) None (println "gone")) (println (len m)) ; gone, then 1 (free m)) @@ -32,7 +32,7 @@ ;; the block is still there and the slots are empty rather than poisoned. (let [m (map-new i32 i32)] (dotimes [i 64] (put m i i)) - (dotimes [i 64] (map-remove! m i)) + (dotimes [i 64] (map-remove m i)) (print (len m)) (println "") ; 0 (put m 7 77) (match (get m 7) (Some v) (do (print v) (println "")) None (println "?")) ; 77 @@ -49,7 +49,7 @@ (let [taken 0] (dotimes [i 2000] (if (= 0 (% i 2)) - (match (map-remove! m i) + (match (map-remove m i) (Some v) (if (= v (* (i64 i) 3)) (set taken (+ taken 1))) None (set taken taken)))) (print taken) (println "")) ; 1000 @@ -72,7 +72,7 @@ ;; on the removal path too and not only on get's. (let [g (map-new Cell i32)] (dotimes [i 20] (dotimes [j 20] (put g (Cell {.x i .y j}) (+ (* i 100) j)))) - (match (map-remove! g (Cell {.x 7 .y 9})) + (match (map-remove g (Cell {.x 7 .y 9})) (Some v) (do (print v) (println "")) ; 709 None (println "?")) (print (has-key? g (Cell {.x 7 .y 9}))) (println "") ; false @@ -83,7 +83,7 @@ (let [s (map-new string i32)] (put s "alpha" 1) (put s "beta" 2) - (match (map-remove! s "alpha") + (match (map-remove s "alpha") (Some v) (do (print v) (println "")) ; 1 None (println "?")) (print (has-key? s "beta")) (println "") ; true @@ -95,9 +95,9 @@ ;; shift, the same way a put that grows invalidates one. (let [m (map-new i32 i32)] (dotimes [i 100] (put m i i)) - (dotimes [i 100] (if (= 0 (% i 3)) (map-remove! m i))) + (dotimes [i 100] (if (= 0 (% i 3)) (map-remove m i))) (let [cur (i64 0) k 0 v 0 seen 0 sum 0] - (while (map-next! m (addr cur) (addr k) (addr v)) + (while (map-next m (addr cur) (addr k) (addr v)) (set seen (+ seen 1)) (if (not (= k v)) (set sum (+ sum 1)))) (print seen) (println "") ; 66 @@ -113,7 +113,7 @@ (with-allocator ar (let [t (map-new i32 i32)] (dotimes [i 300] (put t i (* i 2))) - (dotimes [i 300] (if (= 0 (% i 2)) (map-remove! t i))) + (dotimes [i 300] (if (= 0 (% i 2)) (map-remove t i))) (print (len t)) (println "") ; 150 (match (get t 299) (Some v) (do (print v) (println "")) None (println "?")) ; 598 (print (has-key? t 298)) (println ""))) ; false diff --git a/test/programs/reload-generic.flan b/test/programs/reload-generic.flan index c3131fb..4a8e083 100644 --- a/test/programs/reload-generic.flan +++ b/test/programs/reload-generic.flan @@ -10,13 +10,13 @@ (defvar counter i64) -(defn put! [xs [$t] i i32 v $t] () +(defn put-at [xs [$t] i i32 v $t] () (set (at xs i) v)) -;;; Calls [put!] at its own variable, so the copy of [put!] is generated when -;;; [hold!] is instantiated and not before. -(defn hold! [xs [$t] v $t] () - (put! xs 0 v)) +;;; Calls [put-at] at its own variable, so the copy of [put-at] is generated when +;;; [hold] is instantiated and not before. +(defn hold [xs [$t] v $t] () + (put-at xs 0 v)) (defn pick [xs [$t]] $t {:where (ordered? $t)} @@ -28,8 +28,8 @@ (defn step [] () (let [ns [5 3 9 1] fs [2.5 0.5 1.5]] - (hold! (slice ns 0 4) 7) - (hold! (slice fs 0 3) 0.25) + (hold (slice ns 0 4) 7) + (hold (slice fs 0 3) 0.25) (set counter (+ counter (i64 (pick (slice ns 0 4))))))) (defn main [] () diff --git a/test/programs/slices.flan b/test/programs/slices.flan index f96ada3..c2a5f57 100644 --- a/test/programs/slices.flan +++ b/test/programs/slices.flan @@ -50,33 +50,33 @@ (println "") ; 99 ;; Reverse of an odd-length slice: the middle element stays put. - (reverse! (slice xs 0 (len xs))) + (reverse (slice xs 0 (len xs))) (show (slice xs 0 (len xs))) ; 7 -3 12 0 5 -3 5 ;; And of a two-element one, the smallest case that can actually move. - (reverse! (slice xs 0 2)) + (reverse (slice xs 0 2)) (show (slice xs 0 (len xs))) ; -3 7 12 0 5 -3 5 (load-xs) - (sort! (slice xs 0 (len xs))) + (sort (slice xs 0 (len xs))) (show (slice xs 0 (len xs))) ; -3 -3 0 5 5 7 12 ;; Reverse-sorted: the case a comparison that never fires would pass. (set (at ys 0) 5) (set (at ys 1) 4) (set (at ys 2) 3) (set (at ys 3) 2) (set (at ys 4) 1) - (sort! (slice ys 0 (len ys))) + (sort (slice ys 0 (len ys))) (show (slice ys 0 (len ys))) ; 1 2 3 4 5 ;; A subslice, with the elements on both sides left alone. (set (at zs 0) 100) (set (at zs 1) 9) (set (at zs 2) -1) (set (at zs 3) 9) (set (at zs 4) 4) (set (at zs 5) 0) (set (at zs 6) 200) (set (at zs 7) 300) - (sort! (slice zs 1 6)) + (sort (slice zs 1 6)) (show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300 ;; Degenerate lengths must do nothing rather than run off an end. - (sort! (slice zs 0 0)) - (reverse! (slice zs 0 0)) - (sort! (slice zs 2 3)) - (reverse! (slice zs 2 3)) + (sort (slice zs 0 0)) + (reverse (slice zs 0 0)) + (sort (slice zs 2 3)) + (reverse (slice zs 2 3)) (show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300 0) diff --git a/test/programs/strings.flan b/test/programs/strings.flan index 7e2e989..70d3767 100644 --- a/test/programs/strings.flan +++ b/test/programs/strings.flan @@ -20,12 +20,12 @@ ;; i64->bytes on its own: two of its results cannot be held at once, and ;; these two numbers are both in the answer. (let [b (vec-new u8)] - (append! (addr b) (bytes "x=")) - (append-i64! (addr b) 42) - (append! (addr b) (bytes " y=")) - (append-i64! (addr b) -7) - (append! (addr b) (bytes " r=")) - (append-f64! (addr b) 1.5) + (append (addr b) (bytes "x=")) + (append-i64 (addr b) 42) + (append (addr b) (bytes " y=")) + (append-i64 (addr b) -7) + (append (addr b) (bytes " r=")) + (append-f64 (addr b) 1.5) (show (addr b)) ; x=42 y=-7 r=1.5 (free b)) diff --git a/test/programs/utf8.flan b/test/programs/utf8.flan index 6ad4d8f..a99bcab 100644 --- a/test/programs/utf8.flan +++ b/test/programs/utf8.flan @@ -68,7 +68,7 @@ ;; trip is the only check that catches an encoder and a decoder that are ;; wrong in the same direction — printing the bytes would not. (defn round-trip [code i32] i32 - (match (encode-rune! (slice scratch 0 4) code) + (match (encode-rune (slice scratch 0 4) code) None -1 (Some w) (let [r (decode-rune (slice scratch 0 w))] @@ -82,7 +82,7 @@ (let [it (split-on-byte s sep) going true] (while going - (match (split-next! (addr it)) + (match (split-next (addr it)) (Some f) (do (print "[") (print f) (print "]")) None (set going false))) (print " "))) @@ -181,13 +181,13 @@ (show-i32 (round-trip 0x10ffff)) (println "") - ;; Refused by encode-rune!, and nothing is written when it refuses. - (show-opt (encode-rune! (slice scratch 0 4) 0xd800)) ; -1, surrogate - (show-opt (encode-rune! (slice scratch 0 4) 0x110000)) ; -1, past the end - (show-opt (encode-rune! (slice scratch 0 4) -1)) ; -1, negative - (show-opt (encode-rune! (slice scratch 0 2) 0x65e5)) ; -1, buffer short - (show-opt (encode-rune! (slice scratch 0 0) 0x41)) ; -1, no room at all - (show-opt (encode-rune! (slice scratch 0 1) 0x41)) ; 1, exactly enough + ;; Refused by encode-rune, and nothing is written when it refuses. + (show-opt (encode-rune (slice scratch 0 4) 0xd800)) ; -1, surrogate + (show-opt (encode-rune (slice scratch 0 4) 0x110000)) ; -1, past the end + (show-opt (encode-rune (slice scratch 0 4) -1)) ; -1, negative + (show-opt (encode-rune (slice scratch 0 2) 0x65e5)) ; -1, buffer short + (show-opt (encode-rune (slice scratch 0 0) 0x41)) ; -1, no room at all + (show-opt (encode-rune (slice scratch 0 1) 0x41)) ; 1, exactly enough (println "") ;; "Nothing is written when it refuses" is a claim about the buffer, not @@ -197,9 +197,9 @@ ;; passes. So put a known byte in scratch, ask for an encoding that must be ;; refused, and read the byte back. (show-i32 (round-trip 0x41)) ; 65, scratch[0] = A - (show-opt (encode-rune! (slice scratch 0 2) 0x65e5)) ; -1, needs 3 bytes + (show-opt (encode-rune (slice scratch 0 2) 0x65e5)) ; -1, needs 3 bytes (show-i32 (i32 (at scratch 0))) ; 65 still - (show-opt (encode-rune! (slice scratch 0 4) 0xd800)) ; -1, surrogate + (show-opt (encode-rune (slice scratch 0 4) 0xd800)) ; -1, surrogate (show-i32 (i32 (at scratch 0))) ; 65 still (println "") @@ -224,7 +224,7 @@ total (i64 0) going true] (while going - (match (split-next! (addr it)) + (match (split-next (addr it)) (Some f) (set total (+ total (match (parse-i64 (trim f)) (Some v) v None 0))) None (set going false))) (print total) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 2b1c5e9..78aab49 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -797,7 +797,7 @@ let () = outputs "a number with a precision" "programs/format.flan" format_out; outputs ~opt:"-O0" "a number with a precision, -O0" "programs/format.flan" format_out; - (* The slice family at its second and third element types. sort-i32! was + (* The slice family at its second and third element types. sort-i32 was the only sort in the language; these are copies rather than an abstraction, because map/filter/reduce and a comparator sort all need a function value and check.ml refuses one outright. @@ -2760,9 +2760,9 @@ level "1" and it is named because it is the natural spelling of a set. *) (* Removal is a map's operation and says so, rather than reaching for a [len] that a Vec would also answer. *) - refuses_src "map-remove! wants a map" - "(defn main [] i32 (let [v (vec-new i32)] (map-remove! v 1) (free v)) 0)" - "map-remove! takes a (Map K V)"; + refuses_src "map-remove wants a map" + "(defn main [] i32 (let [v (vec-new i32)] (map-remove v 1) (free v)) 0)" + "map-remove takes a (Map K V)"; refuses_src "a float is not a map key" "(defn f [m (Map f32 i32)] () 0)" "is not a map key"; refuses_src "a Ptr is not a map key" @@ -2860,7 +2860,7 @@ level "1" outputs ~dev:true "function values, dev" "programs/fn-values.flan" fn_values_out; - (* The prelude's four, which is the point of the whole lane: map!, filter, + (* The prelude's four, which is the point of the whole lane: map-in-place, filter, reduce and a comparator sort were blocked on function values and not on generics, so they arrived without generics — and are still one copy per element type, which is the generics half. The f32 rows are that copy. diff --git a/test/test_flan.ml b/test/test_flan.ml index 2719c6c..5d46e1e 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -1925,13 +1925,13 @@ let () = let lines, _ = with_config - { Cimport.no_config with Cimport.renames = [ ("set_seed", "seed!") ] } + { Cimport.no_config with Cimport.renames = [ ("set_seed", "seed") ] } in (* The C symbol is kept verbatim, so an override changes the Flan face and nothing else — which is what makes it safe to spell a predicate the way Lisp spells one. *) check "a name override is the Flan name, and the C symbol is untouched" - (List.mem "(declare-c seed! [seed u32] \"set_seed\")" lines); + (List.mem "(declare-c seed [seed u32] \"set_seed\")" lines); (* The collision above is refused because neither Spin2D nor spin2d may take [spin-2d] by an accident of header order. Naming one of them is the way @@ -2696,9 +2696,9 @@ let () = write. *) rejects_check "a predicate is not carried through a generic call" ~needle:"has to be carried by every signature" - "(defn outer [s [$t]] () {:where (equal? $t)} (sort! s))"; + "(defn outer [s [$t]] () {:where (equal? $t)} (sort s))"; accepts "and is accepted when it is" - "(defn outer [s [$t]] () {:where (ordered? $t)} (sort! s))"; + "(defn outer [s [$t]] () {:where (ordered? $t)} (sort s))"; (* A map key that is a type variable has no hash and no equality to emit: they are chosen from the concrete type, which does not exist yet. So the @@ -2719,9 +2719,9 @@ let () = (* And so does the removal, whose placeholder is [get]'s for the same reason: it answers an (Option V), so the match around it still has to check while the key is a variable. *) - accepts "map-remove! over a type-variable key answers an (Option V)" + accepts "map-remove over a type-variable key answers an (Option V)" "(defn f [m (Map $t i32) k $t] i32 {:where (hashable? $t)} \ - (match (map-remove! m k) (Some v) v _ 0))"; + (match (map-remove m k) (Some v) v _ 0))"; accepts "and so do has-key?, reserve and clone" "(defn f [m (Map $t i32) k $t] bool {:where (hashable? $t)} \ (do (reserve m 8) (let [c (clone m)] (free c) (has-key? m k))))"; diff --git a/test/test_session.ml b/test/test_session.ml index fa8688a..8cc38fb 100644 --- a/test/test_session.ml +++ b/test/test_session.ml @@ -801,9 +801,9 @@ let () = (* 1. [C-c C-c] on a generic used to report [installs=false, fns=[]]: it installed nothing and did not say anything had gone wrong. Both copies - have to be named, and the copy of [put!] that [hold!] pulls in has to be + have to be named, and the copy of [put-at] that [hold] pulls in has to be there too, which is transitivity. *) - (match Session.eval (gen ()) "(defn hold! [xs [$t] v $t] () (put! xs 0 v) (put! xs 0 v))" with + (match Session.eval (gen ()) "(defn hold [xs [$t] v $t] () (put-at xs 0 v) (put-at xs 0 v))" with | c -> if not c.Session.installs then fail "redefining a generic installed nothing"; @@ -812,28 +812,28 @@ let () = if not (List.mem want c.Session.fns) then fail "redefining a generic did not install %s; it installed %s" want (String.concat " " c.Session.fns)) - [ "hold!-i32"; "hold!-f64" ]; - (* And only its own copies: [put!] did not change, and its copies are + [ "hold-i32"; "hold-f64" ]; + (* And only its own copies: [put-at] did not change, and its copies are reached through their cells, so reinstalling them would be work with no effect. *) - if List.mem "put!-i32" c.Session.fns then + if List.mem "put-at-i32" c.Session.fns then fail "redefining a generic reinstalled an unchanged generic's copies" | exception Loc.Error { Loc.dmsg = m; _ } -> fail "redefining a generic: %s" m); - (* The callee side of the same rule: redefining [put!] reinstalls the copies - of [put!], which exist only because [hold!] asked for them — the + (* The callee side of the same rule: redefining [put-at] reinstalls the copies + of [put-at], which exist only because [hold] asked for them — the instantiation that generated them was transitive, and finding them again is one table lookup rather than a walk, because a whole-program check has already regenerated all of them. *) - (match Session.eval (gen ()) "(defn put! [xs [$t] i i32 v $t] () (set (at xs i) v))" with + (match Session.eval (gen ()) "(defn put-at [xs [$t] i i32 v $t] () (set (at xs i) v))" with | c -> List.iter (fun want -> if not (List.mem want c.Session.fns) then fail "redefining a called generic did not install %s; it \ installed %s" want (String.concat " " c.Session.fns)) - [ "put!-i32"; "put!-f64" ] + [ "put-at-i32"; "put-at-f64" ] | exception Loc.Error { Loc.dmsg = m; _ } -> fail "redefining a generic: %s" m); @@ -891,20 +891,20 @@ let () = location — a better error than this one and the reason this path is reached less often than it looks. What reaches here is a change every call site still accepts and every *copy* does not: widening the index - from i32 to i64 leaves [(put! xs 0 v)] checking, because the literal - adapts, and changes [put!-i32]'s signature underneath every compiled + from i32 to i64 leaves [(put-at xs 0 v)] checking, because the literal + adapts, and changes [put-at-i32]'s signature underneath every compiled caller. *) (match Session.eval (gen ()) - "(defn put! [xs [$t] i i64 v $t] () \ + "(defn put-at [xs [$t] i i64 v $t] () \ (set (at xs (i32 i)) v))" with | _ -> fail "a generic's changed parameter type was accepted" | exception Loc.Error { Loc.dmsg = m; _ } -> if not (has m "changes signature") then fail "a generic's changed parameter type: %S" m; - if not (has m "the copy of the generic put!") then - fail "the refusal did not say the name came from put!: %S" m; + if not (has m "the copy of the generic put-at") then + fail "the refusal did not say the name came from put-at: %S" m; if not (has m "every copy of it at once") then fail "the refusal did not say every copy changed together: %S" m); diff --git a/vendor/edn/read.flan b/vendor/edn/read.flan index 85e5d68..5acfcf8 100644 --- a/vendor/edn/read.flan +++ b/vendor/edn/read.flan @@ -87,7 +87,7 @@ ;; would be keeping a handle for an operation that never happens. (defn copy-text [s [u8]] string (let [b (vec-new u8)] - (append! (addr b) s) + (append (addr b) s) (string (as-slice b)))) ;; ── Structural equality ───────────────────────────────────────────── @@ -148,7 +148,7 @@ (let [cur (i64 0) k "" v Value.Nil] - (while (map-next! a (addr cur) (addr k) (addr v)) + (while (map-next a (addr cur) (addr k) (addr v)) (match (get b k) (Some w) (when (not (value=? v w)) (return false)) None (return false)))) diff --git a/vendor/json/json.flan b/vendor/json/json.flan index 31152c1..98daa1f 100644 --- a/vendor/json/json.flan +++ b/vendor/json/json.flan @@ -465,7 +465,7 @@ ;; the last place that knows that offset — string-of runs over a literal this ;; has already accepted, and an error out of it would have to point at the ;; token rather than at the backslash. Surrogate PAIRING is checked here too, -;; and not only escape syntax, so that string-of's encode-rune! can never be +;; and not only escape syntax, so that string-of's encode-rune can never be ;; handed a code point the prelude refuses. (defn read-string [c (Ptr Cursor) lo i32] Token (let [s (.src c) @@ -500,7 +500,7 @@ ;; A high surrogate has to be followed by \u and a low one. ;; A low one on its own, or a high one followed by anything ;; else, encodes no character: rune-size answers None for the - ;; whole D800-DFFF block and encode-rune! writes nothing, so + ;; whole D800-DFFF block and encode-rune writes nothing, so ;; the alternative to refusing here is a silently dropped ;; character later. That refusal is forced by the prelude ;; rather than chosen, and it is the reason the pairing rule @@ -703,11 +703,11 @@ (set r (+ 0x10000 (bit-or (<< (- r 0xd800) 10) (- lo2 0xdc00)))))) ;; A four-byte buffer and not a push per byte, because - ;; encode-rune! is the prelude's answer for this and writing + ;; encode-rune is the prelude's answer for this and writing ;; the shifts again here would be a second copy of UTF-8. (let [buf (array 4 u8)] - (match (encode-rune! (slice buf 0 4) r) - (Some w) (append! (addr b) (slice buf 0 w)) + (match (encode-rune (slice buf 0 4) r) + (Some w) (append (addr b) (slice buf 0 w)) ;; Unreachable: read-string refuses every code point ;; rune-size refuses. Written as a no-op rather than a trap ;; because a dropped character is not worth a crash and the diff --git a/web/index.html b/web/index.html index 15637a1..4607389 100644 --- a/web/index.html +++ b/web/index.html @@ -941,7 +941,7 @@ are four predicates, and each gates builtins the compiler already has:
They entail each other in one direction, so one clause usually does:
numeric? gives ordered?, and ordered? gives
-equal?. A sort! that compares its elements declares
+equal?. A sort that compares its elements declares
ordered? and nothing else.
Every value copies. There used to be a fifth predicate, @@ -1044,13 +1044,13 @@ over.
| Group | Names |
|---|---|
| slice algorithms, over one type variable | swap!, reverse!, sort!, sort-by!, index-of, min-of, max-of, map!, reduce, filter |
| slice algorithms, over one type variable | swap, reverse, sort, sort-by, index-of, min-of, max-of, map-in-place, reduce, filter |
| the per-type layer that stays | sum-i32, sum-f32 — the element and the accumulator are different types, which one variable cannot say |
| bytes | bytes=?, bytes<?, bytes-ci=?, starts-with?, ends-with?, index-of-bytes, trim, digit?, space?, sort-bytes! |
| bytes | bytes=?, bytes<?, bytes-ci=?, starts-with?, ends-with?, index-of-bytes, trim, digit?, space?, sort-bytes |
| parsing | parse-i64, parse-f64 |
| text | split-on-byte, split-next!, split, lower-ascii, upper-ascii, to-lower, to-upper |
| building bytes | append!, append-i64!, append-f64!, concat, join, repeat-bytes, replace-bytes, slices-new, format-f64 |
| UTF-8 | decode-rune, rune-at, rune-count, rune-size, rune-start?, valid-utf8?, encode-rune! |
| text | split-on-byte, split-next, split, lower-ascii, upper-ascii, to-lower, to-upper |
| building bytes | append, append-i64, append-f64, concat, join, repeat-bytes, replace-bytes, slices-new, format-f64 |
| UTF-8 | decode-rune, rune-at, rune-count, rune-size, rune-start?, valid-utf8?, encode-rune |
| numbers | sign-f32, lerp, clamp, floor-f32, ceil-f32, round-f32, abs-i32, abs-i64, the constants pi-f32, pi-f64, tau-f32, tau-f64, and libm through a declare at both widths: sqrt, abs, floor, ceil, round, fmod, sin, cos, tan, asin, acos, atan, atan2, log, log2, log10, exp, pow, hypot, cbrt — each spelled -f32 or -f64 |
| time | monotonic-ns, monotonic-seconds, unix-ns, unix-seconds, sleep-ns, sleep-seconds, and ns-per-second and its two smaller siblings |
| files | file-exists? and file-size, which answer a value; slurp, barf, delete-file, rename-file and make-directory, which signal FileError under retry and use-value |