The ! suffix retires: a mutator is named for what it does, not marked
The !-means-mutates convention distinguished nothing — there is no immutable counterpart to contrast with — so every mutating name drops the mark: sort, sort-by, sort-bytes, swap, reverse, append, append-i64, append-f64, encode-rune, split-next, map-remove, map-next, and the test helpers beside them. Two could not simply shed it: map! is map-in-place, because map is the into transform's word and means the non-mutating thing; put! is put-at, because put is the Map builtin. The ?-means-asks convention stays. Dated records keep the old spellings; watch.clj's reset-spies! and the other Clojure names are not ours to rename.
This commit is contained in:
parent
69c033e946
commit
a0f37e72a2
2
FIX.org
2
FIX.org
@ -151,7 +151,7 @@ against 985ms on LLVM.
|
|||||||
** Open, carried forward
|
** Open, carried forward
|
||||||
- A transient signal -11 on the globals daemon, seen once, not reproduced.
|
- A transient signal -11 on the globals daemon, seen once, not reproduced.
|
||||||
Recorded below.
|
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
|
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*
|
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
|
a value lives, which is what reading an arena-parsed EDN document back would
|
||||||
|
|||||||
20
NEXT.md
20
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
|
### 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
|
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:
|
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.
|
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
|
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
|
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
|
worse here than in most languages because a hot-reload session may have been running for an hour before the call
|
||||||
site is reached.
|
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.**
|
**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`
|
`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
|
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
|
### 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
|
- **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.
|
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`.
|
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.
|
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.
|
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
|
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.
|
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;
|
`(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 —
|
`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`.
|
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
|
`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
|
**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.
|
them wants a language decision.
|
||||||
|
|
||||||
- ~~**`Map` keys and values.**~~ **Iteration is built** — `flan_map_next` and the `map-next!` builtin, exactly the
|
- ~~**`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".
|
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
|
`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
|
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.
|
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,
|
**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
|
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
|
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
|
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
|
`[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
|
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
|
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
|
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.
|
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.
|
`rl/draw-text` is safe for a third reason: the shim's `flan_shim_cstr` copies out of ptr+len before the call.
|
||||||
|
|||||||
@ -2658,7 +2658,7 @@ calls per field, which has no channel to hand on.
|
|||||||
| `(put m k v)` | upsert, `()` |
|
| `(put m k v)` | upsert, `()` |
|
||||||
| `(get m k)` | `(Option V)` — absence is `None` |
|
| `(get m k)` | `(Option V)` — absence is `None` |
|
||||||
| `(has-key? m k)` | `bool`, copying no value — **an addition; the spec does not name it** |
|
| `(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 |
|
| `(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.
|
`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
|
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
|
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
|
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.
|
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
|
`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
|
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
|
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
|
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,
|
`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
|
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
|
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
|
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`.
|
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
|
`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.
|
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
|
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"
|
### 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.
|
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
|
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
|
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
|
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
|
- **`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
|
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
|
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.
|
`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
|
- **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
|
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
|
### 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
|
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
|
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.
|
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
|
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.
|
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
|
`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
|
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
|
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.
|
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]
|
(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.
|
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?`,
|
**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
|
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
|
`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
|
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.
|
is checked with `$t` concrete.
|
||||||
|
|||||||
@ -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.
|
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,
|
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
|
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:
|
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.
|
it is written in the compiler, so there is no file to open.
|
||||||
|
|||||||
BIN
emacs/flan-cnr.elc
Normal file
BIN
emacs/flan-cnr.elc
Normal file
Binary file not shown.
BIN
emacs/flan-dape.elc
Normal file
BIN
emacs/flan-dape.elc
Normal file
Binary file not shown.
BIN
emacs/flan-inspect.elc
Normal file
BIN
emacs/flan-inspect.elc
Normal file
Binary file not shown.
BIN
emacs/flan-lower.elc
Normal file
BIN
emacs/flan-lower.elc
Normal file
Binary file not shown.
BIN
emacs/flan-mode.elc
Normal file
BIN
emacs/flan-mode.elc
Normal file
Binary file not shown.
BIN
emacs/flan-repl.elc
Normal file
BIN
emacs/flan-repl.elc
Normal file
Binary file not shown.
BIN
emacs/flan-watch.elc
Normal file
BIN
emacs/flan-watch.elc
Normal file
Binary file not shown.
BIN
emacs/flan.elc
Normal file
BIN
emacs/flan.elc
Normal file
Binary file not shown.
51
lib/check.ml
51
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
|
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
|
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:
|
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. *)
|
rather than two predicates on one line. *)
|
||||||
let pred_entails ~declared ~wanted =
|
let pred_entails ~declared ~wanted =
|
||||||
String.equal 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
|
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
|
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
|
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. *)
|
site in the corpus. *)
|
||||||
let unconstrained env loc op ~needs (t : Types.t) =
|
let unconstrained env loc op ~needs (t : Types.t) =
|
||||||
if generic_ty t then
|
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)) ])))
|
[ mk loc oty (Tast.If (cond, some, none)) ])))
|
||||||
| _ -> assert false)
|
| _ -> 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
|
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
|
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
|
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
|
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
|
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
|
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.
|
one. Nothing is freed per entry because nothing was allocated per entry. *)
|
||||||
|
| "map-remove" ->
|
||||||
The [!] is the mutation the naming rule asks for ([map-next!], and the
|
|
||||||
note below on the two suffixes). *)
|
|
||||||
| "map-remove!" ->
|
|
||||||
arity loc name 2 args;
|
arity loc name 2 args;
|
||||||
(match args with
|
(match args with
|
||||||
| [ target; k ] ->
|
| [ target; k ] ->
|
||||||
let target = check ctx target in
|
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
|
let k = check ctx ~want:kt k in
|
||||||
(* Deferred exactly as [get] is, and with [None] for the same reason:
|
(* 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
|
the abstract pass still has to check whatever the body does with the
|
||||||
answer. *)
|
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_)
|
expect loc ~want (mk loc (Types.Option vt) Tast.None_)
|
||||||
else
|
else
|
||||||
let hash, eq = key_fns ctx.env loc kt in
|
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)) ])))
|
[ mk loc oty (Tast.If (cond, some, none)) ])))
|
||||||
| _ -> assert false)
|
| _ -> 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
|
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
|
at all: every other map operation addresses one entry by hashing it, and
|
||||||
nothing walked the block.
|
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:
|
i64 the caller owns and the loop reads as one:
|
||||||
|
|
||||||
(let [cur 0 k 0 v 0]
|
(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
|
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
|
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,
|
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. *)
|
and the sizes are still needed because the runtime is type-erased. *)
|
||||||
| "map-next!" ->
|
| "map-next" ->
|
||||||
arity loc name 4 args;
|
arity loc name 4 args;
|
||||||
(match args with
|
(match args with
|
||||||
| [ target; cur; k; v ] ->
|
| [ target; cur; k; v ] ->
|
||||||
let target = check ctx target in
|
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 cur = check ctx ~want:(Types.Ptr (Types.Int Types.I64)) cur in
|
||||||
let k = check ctx ~want:(Types.Ptr kt) k in
|
let k = check ctx ~want:(Types.Ptr kt) k in
|
||||||
let v = check ctx ~want:(Types.Ptr vt) v 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
|
same trust [declare-c] already extends, written at the one site where
|
||||||
somebody had to know the answer anyway.
|
somebody had to know the answer anyway.
|
||||||
|
|
||||||
No marker on the name. A [!] in this language means *mutates*
|
No marker on the name. A [?] in this language means *asks*
|
||||||
([map-next!]) and a [?] means *asks* ([font-valid?]), and this does
|
([font-valid?]), and this does not ask; [zeroed], the nearest
|
||||||
neither; [zeroed], the nearest neighbour — a value conjured rather than
|
neighbour — a value conjured rather than
|
||||||
derived — carries no marker either. [ptr] is the marker: a (Ptr T) only
|
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,
|
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
|
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
|
call site these can be refused at. They are deferred and then always
|
||||||
succeed. That is the cheapest possible membership.
|
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
|
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,
|
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
|
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.
|
generic call site is weaker than at a monomorphic one.
|
||||||
|
|
||||||
A variable already bound by an earlier argument is substituted back into
|
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
|
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
|
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
|
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
|
let cret = subst_ty !subst pret in
|
||||||
if List.exists generic_ty cparams || generic_ty cret then begin
|
if List.exists generic_ty cparams || generic_ty cret then begin
|
||||||
(* One generic function calling another at its *own* variable, seen from
|
(* 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
|
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
|
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
|
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
|
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
|
whole promise of the abstract pass is that a generic's refusals arrive
|
||||||
at its definition; if the predicate were left to the instantiation,
|
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
|
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
|
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,
|
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 \
|
"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 \
|
finds nothing is an answer — and the value comes back as a copy of \
|
||||||
its bytes.");
|
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 \
|
"Removes the entry and answers the value it held, or None if there was \
|
||||||
none.");
|
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 \
|
"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)) ...).");
|
(addr v)) ...).");
|
||||||
("has-key?", "has-key? [(Map K V) K] bool",
|
("has-key?", "has-key? [(Map K V) K] bool",
|
||||||
"Whether the key is present, copying no value — the form a condition \
|
"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
|
| Some l -> List.rev_map (fun (_, _, sym) -> sym) !l
|
||||||
|
|
||||||
(* The generic a symbol came from, and the types it was asked for — [None] for
|
(* 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
|
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
|
say which line the programmer should look at, since [sort-i32] appears
|
||||||
nowhere in the source. *)
|
nowhere in the source. *)
|
||||||
let instantiation_origin env sym =
|
let instantiation_origin env sym =
|
||||||
Hashtbl.fold
|
Hashtbl.fold
|
||||||
|
|||||||
@ -170,8 +170,8 @@ let at loc fmt =
|
|||||||
|
|
||||||
(* ── Names ──────────────────────────────────────────────────────────
|
(* ── Names ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
A Flan name is not a JS identifier: [rand-u32], [bytes=?], [append!] and
|
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
|
[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
|
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
|
second: [-] is the common case and becomes [_], so [rand-u32] reads as
|
||||||
[rand_u32], and an underscore that was actually written becomes [$_] so
|
[rand_u32], and an underscore that was actually written becomes [$_] so
|
||||||
|
|||||||
@ -112,7 +112,7 @@ let rec fields (f : Form.t) (items : Form.t list) : Ast.field list =
|
|||||||
(Form.to_string odd)
|
(Form.to_string odd)
|
||||||
|
|
||||||
(* ── The constraint map at the head of a defn body ──────────────────────
|
(* ── 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
|
[{: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
|
rather than a bare keyword: it leaves room for further keys without new
|
||||||
syntax.
|
syntax.
|
||||||
|
|||||||
@ -219,7 +219,7 @@ let source = {flan|
|
|||||||
;;
|
;;
|
||||||
;; What used to be a copy per element type. A [$t] binds a type variable in
|
;; 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
|
;; 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.
|
;; bodies from one written one.
|
||||||
;;
|
;;
|
||||||
;; **Two things in the signatures are not decoration.**
|
;; **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
|
;; 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
|
;; 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
|
;; kind, and a generic [sum] that took its accumulator and its [+] would just
|
||||||
;; be [reduce]. [append-i64!] and [append-f64!] are two different primitives.
|
;; be [reduce]. [append-i64] and [append-f64] are two different primitives.
|
||||||
;; [sort-bytes!] needs [bytes<?] rather than [<] — a [[u8]] is not [ordered?]
|
;; [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
|
;; 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
|
;; keeps its name because the stability contract in its comment is worth
|
||||||
;; keeping attached to something.
|
;; 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)]
|
(let [t (at s i)]
|
||||||
(set (at s i) (at s j))
|
(set (at s i) (at s j))
|
||||||
(set (at s j) t)))
|
(set (at s j) t)))
|
||||||
|
|
||||||
(defn reverse! [s [$t]] ()
|
(defn reverse [s [$t]] ()
|
||||||
(let [i 0
|
(let [i 0
|
||||||
j (- (len s) 1)]
|
j (- (len s) 1)]
|
||||||
(while (< i j)
|
(while (< i j)
|
||||||
(swap! s i j)
|
(swap s i j)
|
||||||
(set i (+ i 1))
|
(set i (+ i 1))
|
||||||
(set j (- j 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
|
;; 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
|
;; NaNs in the array — there is no ordering of the reals a NaN sits anywhere
|
||||||
;; in.
|
;; in.
|
||||||
(defn sort! [s [$t]] ()
|
(defn sort [s [$t]] ()
|
||||||
{:where (ordered? $t)}
|
{:where (ordered? $t)}
|
||||||
(let [i 1]
|
(let [i 1]
|
||||||
(while (< i (len s))
|
(while (< i (len s))
|
||||||
@ -282,7 +282,7 @@ let source = {flan|
|
|||||||
;; `and` short-circuits, which is load-bearing: at j = 0 the left test
|
;; `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.
|
;; fails and (at s -1) is never evaluated, so this does not trap.
|
||||||
(while (and (> j 0) (> (at s (- j 1)) (at s j)))
|
(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 j (- j 1))))
|
||||||
(set i (+ i 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
|
;; (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.
|
;; 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
|
;; 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
|
;; 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
|
;; 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
|
;; 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
|
;; predicates existed, and it stays because passing a comparison is a real
|
||||||
;; thing to want and not only a workaround.
|
;; 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]
|
(let [i 1]
|
||||||
(while (< i (len s))
|
(while (< i (len s))
|
||||||
(let [j i]
|
(let [j i]
|
||||||
(while (and (> j 0) (before? (at s j) (at s (- j 1))))
|
(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 j (- j 1))))
|
||||||
(set i (+ i 1)))))
|
(set i (+ i 1)))))
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ let source = {flan|
|
|||||||
;;
|
;;
|
||||||
;; **Neither takes a {:where}, and that is a decision rather than an
|
;; **Neither takes a {:where}, and that is a decision rather than an
|
||||||
;; oversight.** A predicate buys an *operation* on the variable — [ordered?]
|
;; 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
|
;; 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
|
;; never touch the payload. That is the one move [ident] in
|
||||||
;; test/programs/generics.flan makes, which needs nothing declared, so these
|
;; 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
|
;; ordinary path. Whether an empty Option is an error is the
|
||||||
;; *caller's* question, and the caller has handler-bind if
|
;; *caller's* question, and the caller has handler-bind if
|
||||||
;; the answer is yes.
|
;; 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
|
;; day something wants it. A macro would get laziness for
|
||||||
;; free and need no generics, and costs more than it buys
|
;; free and need no generics, and costs more than it buys
|
||||||
;; here: a prelude macro drops every prelude defn that
|
;; here: a prelude macro drops every prelude defn that
|
||||||
@ -411,12 +411,12 @@ let source = {flan|
|
|||||||
(defn some? [o (Option $t)] bool
|
(defn some? [o (Option $t)] bool
|
||||||
(match o (Some _v) true None false))
|
(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
|
;; 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
|
;; 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
|
;; here: it is two type variables and a second signature, and nothing has
|
||||||
;; wanted it.
|
;; wanted it.
|
||||||
(defn map! [s [$t] f (Fn [$t] $t)] ()
|
(defn map-in-place [s [$t] f (Fn [$t] $t)] ()
|
||||||
(dotimes [i (len s)]
|
(dotimes [i (len s)]
|
||||||
(set (at s i) (f (at s i)))))
|
(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
|
;; 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
|
;; 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.
|
;; 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)
|
(match (rune-size code)
|
||||||
None None
|
None None
|
||||||
(Some w)
|
(Some w)
|
||||||
@ -1295,7 +1295,7 @@ let source = {flan|
|
|||||||
(defn split-on-byte [s [u8] sep u8] Split
|
(defn split-on-byte [s [u8] sep u8] Split
|
||||||
(Split {.rest s .sep sep .more true}))
|
(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))
|
(when (not (.more it))
|
||||||
(return None))
|
(return None))
|
||||||
(match (index-of (.rest it) (.sep it))
|
(match (index-of (.rest it) (.sep it))
|
||||||
@ -1320,7 +1320,7 @@ let source = {flan|
|
|||||||
;; saying why rather than shipping it. A string
|
;; saying why rather than shipping it. A string
|
||||||
;; literal is emitted `private unnamed_addr constant` (emit.ml), so (bytes
|
;; literal is emitted `private unnamed_addr constant` (emit.ml), so (bytes
|
||||||
;; "Hello") is a [u8] pointing straight into read-only memory. An in-place
|
;; "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,
|
;; on the optimiser — which is the worst of the available answers. Measured,
|
||||||
;; with (set (at (bytes "Hi") 0) \h):
|
;; with (set (at (bytes "Hi") 0) \h):
|
||||||
;;
|
;;
|
||||||
@ -1384,20 +1384,20 @@ let source = {flan|
|
|||||||
(return (< (at a i) (at b i)))))
|
(return (< (at a i) (at b i)))))
|
||||||
(< (len a) (len b))))
|
(< (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
|
;; 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
|
;; fields borrowed from one buffer without touching the buffer. Stable, and
|
||||||
;; here that is observable — two equal fields are two distinct slices of
|
;; 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.
|
;; 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
|
;; 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
|
;; be, because < is defined on machine numbers and comparing two slices
|
||||||
;; lexicographically is a loop and not an instruction. bytes<? is that loop.
|
;; 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
|
;; So this is the shape a generic takes when the operation it needs is not a
|
||||||
;; primitive: pass it in.
|
;; primitive: pass it in.
|
||||||
(defn sort-bytes! [s [[u8]]] ()
|
(defn sort-bytes [s [[u8]]] ()
|
||||||
(sort-by! s (fn [a b] (bytes<? a b))))
|
(sort-by s (fn [a b] (bytes<? a b))))
|
||||||
|
|
||||||
;; ── Building bytes, which is the tier that needed an allocator ────────
|
;; ── 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.
|
;; bytes rather than one, and that is this.
|
||||||
;;
|
;;
|
||||||
;; It takes a (Ptr (Vec u8)) and not a (Vec u8), and the difference is not
|
;; 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.
|
;; 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)]
|
(dotimes [i (len s)]
|
||||||
(push (deref b) (at s i))))
|
(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
|
;; 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
|
;; out of that buffer before returning, so the hazard ends at the call: a
|
||||||
;; builder can hold as many numbers as it likes.
|
;; builder can hold as many numbers as it likes.
|
||||||
(defn append-i64! [b (Ptr (Vec u8)) n i64] ()
|
(defn append-i64 [b (Ptr (Vec u8)) n i64] ()
|
||||||
(append! b (i64->bytes n)))
|
(append b (i64->bytes n)))
|
||||||
|
|
||||||
(defn append-f64! [b (Ptr (Vec u8)) x f64] ()
|
(defn append-f64 [b (Ptr (Vec u8)) x f64] ()
|
||||||
(append! b (f64->bytes x)))
|
(append b (f64->bytes x)))
|
||||||
|
|
||||||
;; concat and join. Both take a slice of slices, which is the shape a caller
|
;; 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
|
;; 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)
|
(defn concat [parts [[u8]]] (Vec u8)
|
||||||
(let [b (vec-new u8)]
|
(let [b (vec-new u8)]
|
||||||
(dotimes [i (len parts)]
|
(dotimes [i (len parts)]
|
||||||
(append! (addr b) (at parts i)))
|
(append (addr b) (at parts i)))
|
||||||
b))
|
b))
|
||||||
|
|
||||||
;; n parts yield n-1 separators, and the empty slice of parts yields the empty
|
;; 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)]
|
(let [b (vec-new u8)]
|
||||||
(dotimes [i (len parts)]
|
(dotimes [i (len parts)]
|
||||||
(when (> i 0)
|
(when (> i 0)
|
||||||
(append! (addr b) sep))
|
(append (addr b) sep))
|
||||||
(append! (addr b) (at parts i)))
|
(append (addr b) (at parts i)))
|
||||||
b))
|
b))
|
||||||
|
|
||||||
(defn repeat-bytes [s [u8] n i32] (Vec u8)
|
(defn repeat-bytes [s [u8] n i32] (Vec u8)
|
||||||
(let [b (vec-new u8)]
|
(let [b (vec-new u8)]
|
||||||
(dotimes [i n]
|
(dotimes [i n]
|
||||||
(append! (addr b) s))
|
(append (addr b) s))
|
||||||
b))
|
b))
|
||||||
|
|
||||||
;; The allocating halves of the ASCII case pair. The note above lower-ascii
|
;; 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)
|
(let [b (vec-new u8)
|
||||||
i 0]
|
i 0]
|
||||||
(if (= (len from) 0)
|
(if (= (len from) 0)
|
||||||
(append! (addr b) s)
|
(append (addr b) s)
|
||||||
(while (< i (len s))
|
(while (< i (len s))
|
||||||
(match (index-of-bytes (slice s i (len s)) from)
|
(match (index-of-bytes (slice s i (len s)) from)
|
||||||
(Some k)
|
(Some k)
|
||||||
(do
|
(do
|
||||||
(append! (addr b) (slice s i (+ i k)))
|
(append (addr b) (slice s i (+ i k)))
|
||||||
(append! (addr b) to)
|
(append (addr b) to)
|
||||||
(set i (+ i k (len from))))
|
(set i (+ i k (len from))))
|
||||||
None
|
None
|
||||||
(do
|
(do
|
||||||
(append! (addr b) (slice s i (len s)))
|
(append (addr b) (slice s i (len s)))
|
||||||
(set i (len s))))))
|
(set i (len s))))))
|
||||||
b))
|
b))
|
||||||
|
|
||||||
@ -1554,7 +1554,7 @@ let source = {flan|
|
|||||||
it (split-on-byte s sep)
|
it (split-on-byte s sep)
|
||||||
going true]
|
going true]
|
||||||
(while going
|
(while going
|
||||||
(match (split-next! (addr it))
|
(match (split-next (addr it))
|
||||||
(Some f) (push v f)
|
(Some f) (push v f)
|
||||||
None (set going false)))
|
None (set going false)))
|
||||||
v))
|
v))
|
||||||
@ -1572,7 +1572,7 @@ let source = {flan|
|
|||||||
;; This returns a Vec, so neither problem is inherited. It uses i64->bytes
|
;; 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
|
;; 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
|
;; 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
|
;; 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
|
;; 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)]
|
p (clamp prec 0 9)]
|
||||||
(cond
|
(cond
|
||||||
(not (= x x))
|
(not (= x x))
|
||||||
(append! (addr b) (bytes "nan"))
|
(append (addr b) (bytes "nan"))
|
||||||
|
|
||||||
(and (= x (* x 2.0)) (!= x 0.0))
|
(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
|
:else
|
||||||
(let [neg (< x 0.0)
|
(let [neg (< x 0.0)
|
||||||
m (if neg (- 0.0 x) x)]
|
m (if neg (- 0.0 x) x)]
|
||||||
(if (>= m 9.0e18)
|
(if (>= m 9.0e18)
|
||||||
(append! (addr b) (f64->bytes x))
|
(append (addr b) (f64->bytes x))
|
||||||
(let [scale (i64 1)]
|
(let [scale (i64 1)]
|
||||||
(dotimes [i p]
|
(dotimes [i p]
|
||||||
(set scale (* scale 10)))
|
(set scale (* scale 10)))
|
||||||
@ -1642,7 +1642,7 @@ let source = {flan|
|
|||||||
;; i64->bytes of 0 has no sign to carry.
|
;; i64->bytes of 0 has no sign to carry.
|
||||||
(when neg
|
(when neg
|
||||||
(push b \-))
|
(push b \-))
|
||||||
(append-i64! (addr b) ip)
|
(append-i64 (addr b) ip)
|
||||||
(when (> p 0)
|
(when (> p 0)
|
||||||
(push b \.)
|
(push b \.)
|
||||||
;; Left-padded with zeros to exactly p digits. fr is under
|
;; Left-padded with zeros to exactly p digits. fr is under
|
||||||
@ -1651,7 +1651,7 @@ let source = {flan|
|
|||||||
(let [d (i64->bytes fr)]
|
(let [d (i64->bytes fr)]
|
||||||
(dotimes [i (- p (len d))]
|
(dotimes [i (- p (len d))]
|
||||||
(push b \0))
|
(push b \0))
|
||||||
(append! (addr b) d))))))))
|
(append (addr b) d))))))))
|
||||||
b))
|
b))
|
||||||
|
|
||||||
;; ── Still refused, and what the reason is now ─────────────────────────
|
;; ── 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
|
;; allocation one. format-f64 above is the piece of it
|
||||||
;; that was actually wanted, and `print`/`println` are
|
;; that was actually wanted, and `print`/`println` are
|
||||||
;; already the structural walk over any one value.
|
;; already the structural walk over any one value.
|
||||||
;; map that changes the Generics, and only that. map!, filter, reduce and
|
;; 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
|
;; element type sort-by landed the day function values did — see
|
||||||
;; "The ones that take a function" above — at i32 and
|
;; "The ones that take a function" above — at i32 and
|
||||||
;; f32, the two element types the rest of that family
|
;; f32, the two element types the rest of that family
|
||||||
;; covers. A map from [i32] to [f32] is the one shape
|
;; 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.
|
;; which is where a per-type family stops being honest.
|
||||||
;; map-keys, map-values Generics — and the reason changed, which is the
|
;; map-keys, map-values Generics — and the reason changed, which is the
|
||||||
;; point of naming them separately. It used to be 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
|
;; and walking a map is expressible now. What a defn
|
||||||
;; still cannot say is (defn map-keys [m {K V}] (Vec K)):
|
;; still cannot say is (defn map-keys [m {K V}] (Vec K)):
|
||||||
;; a prelude function has to name its types, and there
|
;; 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
|
;; already has push, so the struct would be a move-only
|
||||||
;; wrapper whose only method is the one it wraps. What
|
;; wrapper whose only method is the one it wraps. What
|
||||||
;; was missing was appending a run of bytes, and
|
;; was missing was appending a run of bytes, and
|
||||||
;; `append!` above is that.
|
;; `append` above is that.
|
||||||
;; ── Files: embedding, slurp and barf ──────────────────────────────────
|
;; ── Files: embedding, slurp and barf ──────────────────────────────────
|
||||||
;;
|
;;
|
||||||
;; One entry per file in an (embed-dir "...") — Odin's Load_Directory_File
|
;; One entry per file in an (embed-dir "...") — Odin's Load_Directory_File
|
||||||
|
|||||||
@ -206,11 +206,11 @@ let compatible ?(origin = fun _ -> None) ~loc (old_ : Tast.program)
|
|||||||
plan.org, Hot reload, and open decision #6. *)
|
plan.org, Hot reload, and open decision #6. *)
|
||||||
if not same then
|
if not same then
|
||||||
(* ── When the name is not one the programmer wrote ──────────────
|
(* ── 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*
|
and so on, and the mangling carries only the *type variables*
|
||||||
— so editing the generic's other parameters changes every copy's
|
— so editing the generic's other parameters changes every copy's
|
||||||
signature at once, under the same names. The refusal then
|
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.
|
being edited, for a reason invisible at the edited line.
|
||||||
|
|
||||||
So the refusal says where the name came from: which generic, at
|
So the refusal says where the name came from: which generic, at
|
||||||
|
|||||||
4
plan.org
4
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
|
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)~,
|
*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
|
~(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.
|
prelude's per-type families into one function each.
|
||||||
A generic body is checked *abstractly*, with nothing substituted, so ~=~, ~<~,
|
A generic body is checked *abstractly*, with nothing substituted, so ~=~, ~<~,
|
||||||
~+~ and ~hash~ over an unconstrained variable are rejected at the definition
|
~+~ 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,
|
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
|
~{:where [(copyable? $t) (copyable? $u)]}~ — on the precedent of Clojure's
|
||||||
~{:pre ... :post ...}~, and because a bare ~{}~ in expression position is
|
~{: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
|
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
|
checks the concrete type satisfies the predicate and refuses the call site if it
|
||||||
does not. There are *five* predicates — ~ordered?~, ~equal?~, ~hashable?~,
|
does not. There are *five* predicates — ~ordered?~, ~equal?~, ~hashable?~,
|
||||||
|
|||||||
@ -975,7 +975,7 @@ struct flan_allocator {
|
|||||||
*
|
*
|
||||||
* Every size this file computes is a signed 64-bit count of bytes, and every
|
* 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
|
* 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
|
* 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
|
* 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
|
* 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; }
|
if (cap > (int64_t)1 << 40) { cap = want; break; }
|
||||||
cap *= 2;
|
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
|
* 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
|
* is the one the program picked times the one the checker did. See the note
|
||||||
* on flan_mul_bytes. */
|
* on flan_mul_bytes. */
|
||||||
|
|||||||
@ -263,7 +263,7 @@ move concept it opted out of.) plan.org's Types section has the full
|
|||||||
account.
|
account.
|
||||||
|
|
||||||
```
|
```
|
||||||
(defn sort! [s [$t]] ()
|
(defn sort [s [$t]] ()
|
||||||
{:where (ordered? $t)}
|
{:where (ordered? $t)}
|
||||||
...)
|
...)
|
||||||
```
|
```
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
;;;; The slice family at its second and third element types.
|
;;;; 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
|
;;;; 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
|
;;;; taking a comparator all need a *function value*, which check.ml refuses
|
||||||
;;;; with "a function type is not implemented yet -- milestone 5". So the
|
;;;; with "a function type is not implemented yet -- milestone 5". So the
|
||||||
@ -22,41 +22,41 @@
|
|||||||
(defn main [] i32
|
(defn main [] i32
|
||||||
;; Every float literal is cast. A literal defaults to f64 and an array
|
;; 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 --
|
;; 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.
|
;; 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.
|
;; 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)]]
|
(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
|
(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
|
;; 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
|
;; is the whole content of the in-place claim, and a version that copied
|
||||||
;; would pass every test above and fail this one.
|
;; 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)]]
|
(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
|
(show-f32 (slice xs 0 6))) ; 9 1 2 3 4 9
|
||||||
|
|
||||||
;; Already sorted, reverse sorted, and a single element -- the three inputs
|
;; Already sorted, reverse sorted, and a single element -- the three inputs
|
||||||
;; where an insertion loop with the comparison the wrong way round still
|
;; where an insertion loop with the comparison the wrong way round still
|
||||||
;; looks plausible.
|
;; looks plausible.
|
||||||
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0)]]
|
(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
|
(show-f32 (slice xs 0 3))) ; 1 2 3
|
||||||
(let [xs [(f32 3.0) (f32 2.0) (f32 1.0)]]
|
(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
|
(show-f32 (slice xs 0 3))) ; 1 2 3
|
||||||
(let [xs [(f32 7.0)]]
|
(let [xs [(f32 7.0)]]
|
||||||
(sort! (slice xs 0 1))
|
(sort (slice xs 0 1))
|
||||||
(show-f32 (slice xs 0 1))) ; 7
|
(show-f32 (slice xs 0 1))) ; 7
|
||||||
;; The empty slice must not read (at s -1).
|
;; The empty slice must not read (at s -1).
|
||||||
(let [xs [(f32 7.0)]]
|
(let [xs [(f32 7.0)]]
|
||||||
(sort! (slice xs 0 0))
|
(sort (slice xs 0 0))
|
||||||
(show-f32 (slice xs 0 0))) ;
|
(show-f32 (slice xs 0 0))) ;
|
||||||
|
|
||||||
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0) (f32 4.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
|
(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
|
;; 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
|
(print (bytes<? (slice hi 0 1) (slice lo 0 1))) ; false
|
||||||
(println ""))
|
(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 --
|
;; 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.
|
;; which an in-place byte sort could not, since a literal lives in .rodata.
|
||||||
(let [f (split (bytes "pear,apple,Fig,apple,banana") \,)]
|
(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
|
(show-fields (as-slice f)) ; Fig apple apple banana pear
|
||||||
(free f))
|
(free f))
|
||||||
|
|
||||||
;; And the round trip the whole second tier is for: split, sort, join.
|
;; And the round trip the whole second tier is for: split, sort, join.
|
||||||
(let [f (split (bytes "delta,alpha,charlie,bravo") \,)]
|
(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 " < "))]
|
(let [j (join (as-slice f) (bytes " < "))]
|
||||||
(println (string (as-slice j))) ; alpha < bravo < charlie < delta
|
(println (string (as-slice j))) ; alpha < bravo < charlie < delta
|
||||||
(free j))
|
(free j))
|
||||||
|
|||||||
@ -60,7 +60,7 @@
|
|||||||
(dotimes [i (len items)]
|
(dotimes [i (len items)]
|
||||||
(set n (+ n (count-leaves (at items i)))))
|
(set n (+ n (count-leaves (at items i)))))
|
||||||
n)
|
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
|
;; 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
|
;; 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
|
;; here owns anything, the arena does — so walking a map is the ordinary
|
||||||
@ -70,7 +70,7 @@
|
|||||||
cur (i64 0)
|
cur (i64 0)
|
||||||
k ""
|
k ""
|
||||||
v edn/Value.Nil]
|
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))))
|
(set n (+ n (count-leaves v))))
|
||||||
n)
|
n)
|
||||||
_ 1))
|
_ 1))
|
||||||
|
|||||||
@ -86,10 +86,10 @@
|
|||||||
|
|
||||||
;; A document in a buffer this program owns and can write to. (bytes "literal")
|
;; 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 —
|
;; 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 [] ()
|
(defn survives-its-buffer [] ()
|
||||||
(let [buf (vec-new u8)]
|
(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)
|
(let [src (as-slice buf)
|
||||||
v (edn/read src)]
|
v (edn/read src)]
|
||||||
(dotimes [i (len src)]
|
(dotimes [i (len src)]
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
;; The shape map/filter/reduce want: the function arrives as a parameter, is
|
;; The shape map/filter/reduce want: the function arrives as a parameter, is
|
||||||
;; called, and is never stored.
|
;; 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)]
|
(dotimes [i (len xs)]
|
||||||
(set (at xs i) (f (at xs i)))))
|
(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
|
;; 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
|
;; told the order rather than having it written in. Insertion sort, because the
|
||||||
;; point here is the parameter and not the algorithm.
|
;; 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)]
|
(dotimes [i (len xs)]
|
||||||
(let [j i]
|
(let [j i]
|
||||||
(while (and (> j 0) (before? (at xs j) (at xs (- j 1))))
|
(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))))))
|
(set j (- j 1))))))
|
||||||
|
|
||||||
(defn ascending [a i32 b i32] bool (< a b))
|
(defn ascending [a i32 b i32] bool (< a b))
|
||||||
@ -62,7 +62,7 @@
|
|||||||
(defn main [] i32
|
(defn main [] i32
|
||||||
(let [xs [1 2 3 4]]
|
(let [xs [1 2 3 4]]
|
||||||
;; A name in value position, passed down.
|
;; 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 "")
|
(print (at xs 0)) (print " ") (print (at xs 3)) (println "")
|
||||||
;; 2 + 4 + 6 + 8 negated
|
;; 2 + 4 + 6 + 8 negated
|
||||||
(println (fold (slice xs 0 4) negate))
|
(println (fold (slice xs 0 4) negate))
|
||||||
@ -75,12 +75,12 @@
|
|||||||
;; A comparator, and the same slice sorted both ways.
|
;; A comparator, and the same slice sorted both ways.
|
||||||
(let [ys [3 1 4 1 5 9 2 6]
|
(let [ys [3 1 4 1 5 9 2 6]
|
||||||
s (slice ys 0 8)]
|
s (slice ys 0 8)]
|
||||||
(insertion-by! s ascending)
|
(insertion-by s ascending)
|
||||||
(print (at s 0)) (print " ") (print (at s 7)) (println "")
|
(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 "")
|
(print (at s 0)) (print " ") (print (at s 7)) (println "")
|
||||||
;; A returned function value, and a computed head calling it.
|
;; 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 "")
|
(print (at s 0)) (println "")
|
||||||
(println ((pick false) 1 2)))
|
(println ((pick false) 1 2)))
|
||||||
|
|
||||||
|
|||||||
@ -87,13 +87,13 @@
|
|||||||
;; needs the integer part copied out before the fraction is rendered, because
|
;; needs the integer part copied out before the fraction is rendered, because
|
||||||
;; both come through the runtime's one shared scratch buffer.
|
;; both come through the runtime's one shared scratch buffer.
|
||||||
(let [b (vec-new u8)]
|
(let [b (vec-new u8)]
|
||||||
(append! (addr b) (bytes "fps "))
|
(append (addr b) (bytes "fps "))
|
||||||
(let [f (format-f64 59.94 1)]
|
(let [f (format-f64 59.94 1)]
|
||||||
(append! (addr b) (as-slice f))
|
(append (addr b) (as-slice f))
|
||||||
(free f))
|
(free f))
|
||||||
(append! (addr b) (bytes " / frame "))
|
(append (addr b) (bytes " / frame "))
|
||||||
(let [f (format-f64 0.0166667 4)]
|
(let [f (format-f64 0.0166667 4)]
|
||||||
(append! (addr b) (as-slice f))
|
(append (addr b) (as-slice f))
|
||||||
(free f))
|
(free f))
|
||||||
(println (string (as-slice b))) ; fps 59.9 / frame 0.0167
|
(println (string (as-slice b))) ; fps 59.9 / frame 0.0167
|
||||||
(free b))
|
(free b))
|
||||||
|
|||||||
@ -25,11 +25,11 @@
|
|||||||
(defn first-or [s [$t] d $t] $t
|
(defn first-or [s [$t] d $t] $t
|
||||||
(if (= (len s) 0) d (at s 0)))
|
(if (= (len s) 0) d (at s 0)))
|
||||||
|
|
||||||
;; A generic calling a generic at its own variable: the copy of [swap!] is
|
;; A generic calling a generic at its own variable: the copy of [swap] is
|
||||||
;; generated when [rotate!] is instantiated and not before.
|
;; generated when [rotate] is instantiated and not before.
|
||||||
(defn rotate! [s [$t]] ()
|
(defn rotate [s [$t]] ()
|
||||||
(dotimes [i (- (len s) 1)]
|
(dotimes [i (- (len s) 1)]
|
||||||
(swap! s i (+ i 1))))
|
(swap s i (+ i 1))))
|
||||||
|
|
||||||
;; numeric? admits + - * / %.
|
;; numeric? admits + - * / %.
|
||||||
(defn twice [x $t] $t
|
(defn twice [x $t] $t
|
||||||
@ -121,7 +121,7 @@
|
|||||||
fs [2.5 0.5 1.5]]
|
fs [2.5 0.5 1.5]]
|
||||||
(println (first-or (slice ns 0 4) -1))
|
(println (first-or (slice ns 0 4) -1))
|
||||||
(println (first-or (slice ns 0 0) -1))
|
(println (first-or (slice ns 0 0) -1))
|
||||||
(rotate! (slice ns 0 4))
|
(rotate (slice ns 0 4))
|
||||||
(println (at ns 3))
|
(println (at ns 3))
|
||||||
|
|
||||||
(println (twice 21))
|
(println (twice 21))
|
||||||
@ -136,13 +136,13 @@
|
|||||||
(show "text")
|
(show "text")
|
||||||
|
|
||||||
;; The collapsed prelude family, at both element types.
|
;; The collapsed prelude family, at both element types.
|
||||||
(sort! (slice ns 0 4))
|
(sort (slice ns 0 4))
|
||||||
(println (at ns 0))
|
(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))
|
(println (at fs 0))
|
||||||
(reverse! (slice ns 0 4))
|
(reverse (slice ns 0 4))
|
||||||
(println (at ns 0))
|
(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))))
|
(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 (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))
|
(match (max-of (slice fs 0 3)) (Some m) (println m) _ (println -1.0))
|
||||||
|
|||||||
@ -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
|
;; 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
|
;; 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
|
;; prelude functions, called the ordinary way, with the function passed by
|
||||||
@ -12,10 +12,10 @@
|
|||||||
(defn big? [x f32] bool (> x 1.0))
|
(defn big? [x f32] bool (> x 1.0))
|
||||||
|
|
||||||
(defn main [] i32
|
(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]
|
(let [xs [1 2 3 4]
|
||||||
s (slice xs 0 4)]
|
s (slice xs 0 4)]
|
||||||
(map! s triple)
|
(map-in-place s triple)
|
||||||
(print (at s 0)) (print " ") (print (at s 3)) (println "")
|
(print (at s 0)) (print " ") (print (at s 3)) (println "")
|
||||||
|
|
||||||
;; reduce, with the accumulator first in the step. The prelude's own
|
;; reduce, with the accumulator first in the step. The prelude's own
|
||||||
@ -30,21 +30,21 @@
|
|||||||
(free v))
|
(free v))
|
||||||
|
|
||||||
;; A comparator sort, both directions off the same slice.
|
;; 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 "")
|
(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 ""))
|
(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
|
;; The f32 half of the family, which is the same code at the other element
|
||||||
;; type — the copy that generics would remove.
|
;; type — the copy that generics would remove.
|
||||||
(let [ys [(f32 4.0) (f32 1.0) (f32 8.0) (f32 2.0)]
|
(let [ys [(f32 4.0) (f32 1.0) (f32 8.0) (f32 2.0)]
|
||||||
t (slice ys 0 4)]
|
t (slice ys 0 4)]
|
||||||
(map! t halve)
|
(map-in-place t halve)
|
||||||
(print (at t 0)) (print " ") (print (at t 2)) (println "")
|
(print (at t 0)) (print " ") (print (at t 2)) (println "")
|
||||||
(print (reduce t 0.0 (fn [a b] (+ a b)))) (println "")
|
(print (reduce t 0.0 (fn [a b] (+ a b)))) (println "")
|
||||||
(let [w (filter t big?)]
|
(let [w (filter t big?)]
|
||||||
(print (len w)) (println "")
|
(print (len w)) (println "")
|
||||||
(free w))
|
(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 ""))
|
(print (at t 0)) (print " ") (print (at t 3)) (println ""))
|
||||||
0)
|
0)
|
||||||
|
|||||||
@ -208,7 +208,7 @@
|
|||||||
(dotimes [i (len items)]
|
(dotimes [i (len items)]
|
||||||
(set n (+ n (count-leaves (at items i)))))
|
(set n (+ n (count-leaves (at items i)))))
|
||||||
n)
|
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.
|
;; 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
|
;; 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.
|
;; the ordinary iteration and needs no accessor of its own.
|
||||||
@ -217,7 +217,7 @@
|
|||||||
cur (i64 0)
|
cur (i64 0)
|
||||||
k ""
|
k ""
|
||||||
e Value.Null]
|
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))))
|
(set n (+ n (count-leaves e))))
|
||||||
n)
|
n)
|
||||||
_ 1))
|
_ 1))
|
||||||
@ -347,7 +347,7 @@
|
|||||||
|
|
||||||
;; Strings: the escape grammar, and the two surrogate halves. A lone
|
;; Strings: the escape grammar, and the two surrogate halves. A lone
|
||||||
;; surrogate is refused because rune-size answers None for the whole
|
;; 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.
|
;; would vanish — the refusal is forced by the prelude rather than chosen.
|
||||||
(refusal "\"a\\vb\"") ; \v is JSON5's
|
(refusal "\"a\\vb\"") ; \v is JSON5's
|
||||||
(refusal "\"a\\x41b\"") ; so is \x
|
(refusal "\"a\\x41b\"") ; so is \x
|
||||||
@ -405,7 +405,7 @@
|
|||||||
;; two lifetimes have to be separable for the scribble below to mean
|
;; two lifetimes have to be separable for the scribble below to mean
|
||||||
;; anything.
|
;; anything.
|
||||||
(let [buf (vec-new u8)]
|
(let [buf (vec-new u8)]
|
||||||
(append! (addr buf) (bytes doc))
|
(append (addr buf) (bytes doc))
|
||||||
(with-allocator frame
|
(with-allocator frame
|
||||||
(let [v (read-doc (as-slice buf))]
|
(let [v (read-doc (as-slice buf))]
|
||||||
(println (describe v)) ; object
|
(println (describe v)) ; object
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
keys 0
|
keys 0
|
||||||
vals 0
|
vals 0
|
||||||
n 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 keys (+ keys k))
|
||||||
(set vals (+ vals v))
|
(set vals (+ vals v))
|
||||||
(set n (+ n 1)))
|
(set n (+ n 1)))
|
||||||
@ -35,12 +35,12 @@
|
|||||||
k 0
|
k 0
|
||||||
v 0
|
v 0
|
||||||
n 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)))
|
(set n (+ n 1)))
|
||||||
(print "never allocated: ") (print n) (println "")
|
(print "never allocated: ") (print n) (println "")
|
||||||
(reserve m 64)
|
(reserve m 64)
|
||||||
(set cur (i64 0))
|
(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)))
|
(set n (+ n 1)))
|
||||||
(print "allocated and empty: ") (print n) (println "")
|
(print "allocated and empty: ") (print n) (println "")
|
||||||
(free m)))
|
(free m)))
|
||||||
@ -52,9 +52,9 @@
|
|||||||
(put m 5 50)
|
(put m 5 50)
|
||||||
(put m 6 60)
|
(put m 6 60)
|
||||||
(let [cur (i64 0) k 0 v 0 n 0]
|
(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)))
|
(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)))
|
(set n (+ n 1)))
|
||||||
(print "spent: ") (print n) (println ""))
|
(print "spent: ") (print n) (println ""))
|
||||||
(free m)))
|
(free m)))
|
||||||
@ -75,7 +75,7 @@
|
|||||||
chars 0
|
chars 0
|
||||||
xs 0
|
xs 0
|
||||||
ys 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 chars (+ chars (len k)))
|
||||||
(set xs (+ xs (.x v)))
|
(set xs (+ xs (.x v)))
|
||||||
(set ys (+ ys (.y v))))
|
(set ys (+ ys (.y v))))
|
||||||
@ -94,7 +94,7 @@
|
|||||||
v (i64 0)
|
v (i64 0)
|
||||||
n 0
|
n 0
|
||||||
doubled 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))
|
(set n (+ n 1))
|
||||||
(when (= v (* k 2)) (set doubled (+ doubled 1))))
|
(when (= v (* k 2)) (set doubled (+ doubled 1))))
|
||||||
(print n) (print " ") (print doubled) (print " ") (print (len m)) (println ""))
|
(print n) (print " ") (print doubled) (print " ") (print (len m)) (println ""))
|
||||||
|
|||||||
@ -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
|
;;;; 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
|
;;;; 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)]
|
(let [m (map-new i32 i64)]
|
||||||
(put m 1 100)
|
(put m 1 100)
|
||||||
(put m 2 200)
|
(put m 2 200)
|
||||||
(match (map-remove! m 1)
|
(match (map-remove m 1)
|
||||||
(Some v) (do (print v) (println "")) ; 100
|
(Some v) (do (print v) (println "")) ; 100
|
||||||
None (println "missing"))
|
None (println "missing"))
|
||||||
(print (len m)) (println "") ; 1
|
(print (len m)) (println "") ; 1
|
||||||
(print (has-key? m 1)) (println "") ; false
|
(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
|
(println (len m)) ; gone, then 1
|
||||||
(free m))
|
(free m))
|
||||||
|
|
||||||
@ -32,7 +32,7 @@
|
|||||||
;; the block is still there and the slots are empty rather than poisoned.
|
;; the block is still there and the slots are empty rather than poisoned.
|
||||||
(let [m (map-new i32 i32)]
|
(let [m (map-new i32 i32)]
|
||||||
(dotimes [i 64] (put m i i))
|
(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
|
(print (len m)) (println "") ; 0
|
||||||
(put m 7 77)
|
(put m 7 77)
|
||||||
(match (get m 7) (Some v) (do (print v) (println "")) None (println "?")) ; 77
|
(match (get m 7) (Some v) (do (print v) (println "")) None (println "?")) ; 77
|
||||||
@ -49,7 +49,7 @@
|
|||||||
(let [taken 0]
|
(let [taken 0]
|
||||||
(dotimes [i 2000]
|
(dotimes [i 2000]
|
||||||
(if (= 0 (% i 2))
|
(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)))
|
(Some v) (if (= v (* (i64 i) 3)) (set taken (+ taken 1)))
|
||||||
None (set taken taken))))
|
None (set taken taken))))
|
||||||
(print taken) (println "")) ; 1000
|
(print taken) (println "")) ; 1000
|
||||||
@ -72,7 +72,7 @@
|
|||||||
;; on the removal path too and not only on get's.
|
;; on the removal path too and not only on get's.
|
||||||
(let [g (map-new Cell i32)]
|
(let [g (map-new Cell i32)]
|
||||||
(dotimes [i 20] (dotimes [j 20] (put g (Cell {.x i .y j}) (+ (* i 100) j))))
|
(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
|
(Some v) (do (print v) (println "")) ; 709
|
||||||
None (println "?"))
|
None (println "?"))
|
||||||
(print (has-key? g (Cell {.x 7 .y 9}))) (println "") ; false
|
(print (has-key? g (Cell {.x 7 .y 9}))) (println "") ; false
|
||||||
@ -83,7 +83,7 @@
|
|||||||
(let [s (map-new string i32)]
|
(let [s (map-new string i32)]
|
||||||
(put s "alpha" 1)
|
(put s "alpha" 1)
|
||||||
(put s "beta" 2)
|
(put s "beta" 2)
|
||||||
(match (map-remove! s "alpha")
|
(match (map-remove s "alpha")
|
||||||
(Some v) (do (print v) (println "")) ; 1
|
(Some v) (do (print v) (println "")) ; 1
|
||||||
None (println "?"))
|
None (println "?"))
|
||||||
(print (has-key? s "beta")) (println "") ; true
|
(print (has-key? s "beta")) (println "") ; true
|
||||||
@ -95,9 +95,9 @@
|
|||||||
;; shift, the same way a put that grows invalidates one.
|
;; shift, the same way a put that grows invalidates one.
|
||||||
(let [m (map-new i32 i32)]
|
(let [m (map-new i32 i32)]
|
||||||
(dotimes [i 100] (put m i i))
|
(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]
|
(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))
|
(set seen (+ seen 1))
|
||||||
(if (not (= k v)) (set sum (+ sum 1))))
|
(if (not (= k v)) (set sum (+ sum 1))))
|
||||||
(print seen) (println "") ; 66
|
(print seen) (println "") ; 66
|
||||||
@ -113,7 +113,7 @@
|
|||||||
(with-allocator ar
|
(with-allocator ar
|
||||||
(let [t (map-new i32 i32)]
|
(let [t (map-new i32 i32)]
|
||||||
(dotimes [i 300] (put t i (* i 2)))
|
(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
|
(print (len t)) (println "") ; 150
|
||||||
(match (get t 299) (Some v) (do (print v) (println "")) None (println "?")) ; 598
|
(match (get t 299) (Some v) (do (print v) (println "")) None (println "?")) ; 598
|
||||||
(print (has-key? t 298)) (println ""))) ; false
|
(print (has-key? t 298)) (println ""))) ; false
|
||||||
|
|||||||
@ -10,13 +10,13 @@
|
|||||||
|
|
||||||
(defvar counter i64)
|
(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))
|
(set (at xs i) v))
|
||||||
|
|
||||||
;;; Calls [put!] at its own variable, so the copy of [put!] is generated when
|
;;; Calls [put-at] at its own variable, so the copy of [put-at] is generated when
|
||||||
;;; [hold!] is instantiated and not before.
|
;;; [hold] is instantiated and not before.
|
||||||
(defn hold! [xs [$t] v $t] ()
|
(defn hold [xs [$t] v $t] ()
|
||||||
(put! xs 0 v))
|
(put-at xs 0 v))
|
||||||
|
|
||||||
(defn pick [xs [$t]] $t
|
(defn pick [xs [$t]] $t
|
||||||
{:where (ordered? $t)}
|
{:where (ordered? $t)}
|
||||||
@ -28,8 +28,8 @@
|
|||||||
(defn step [] ()
|
(defn step [] ()
|
||||||
(let [ns [5 3 9 1]
|
(let [ns [5 3 9 1]
|
||||||
fs [2.5 0.5 1.5]]
|
fs [2.5 0.5 1.5]]
|
||||||
(hold! (slice ns 0 4) 7)
|
(hold (slice ns 0 4) 7)
|
||||||
(hold! (slice fs 0 3) 0.25)
|
(hold (slice fs 0 3) 0.25)
|
||||||
(set counter (+ counter (i64 (pick (slice ns 0 4)))))))
|
(set counter (+ counter (i64 (pick (slice ns 0 4)))))))
|
||||||
|
|
||||||
(defn main [] ()
|
(defn main [] ()
|
||||||
|
|||||||
@ -50,33 +50,33 @@
|
|||||||
(println "") ; 99
|
(println "") ; 99
|
||||||
|
|
||||||
;; Reverse of an odd-length slice: the middle element stays put.
|
;; 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
|
(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.
|
;; 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
|
(show (slice xs 0 (len xs))) ; -3 7 12 0 5 -3 5
|
||||||
|
|
||||||
(load-xs)
|
(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
|
(show (slice xs 0 (len xs))) ; -3 -3 0 5 5 7 12
|
||||||
|
|
||||||
;; Reverse-sorted: the case a comparison that never fires would pass.
|
;; 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 0) 5) (set (at ys 1) 4) (set (at ys 2) 3)
|
||||||
(set (at ys 3) 2) (set (at ys 4) 1)
|
(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
|
(show (slice ys 0 (len ys))) ; 1 2 3 4 5
|
||||||
|
|
||||||
;; A subslice, with the elements on both sides left alone.
|
;; 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 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 3) 9) (set (at zs 4) 4) (set (at zs 5) 0)
|
||||||
(set (at zs 6) 200) (set (at zs 7) 300)
|
(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
|
(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.
|
;; Degenerate lengths must do nothing rather than run off an end.
|
||||||
(sort! (slice zs 0 0))
|
(sort (slice zs 0 0))
|
||||||
(reverse! (slice zs 0 0))
|
(reverse (slice zs 0 0))
|
||||||
(sort! (slice zs 2 3))
|
(sort (slice zs 2 3))
|
||||||
(reverse! (slice zs 2 3))
|
(reverse (slice zs 2 3))
|
||||||
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
|
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
|
||||||
0)
|
0)
|
||||||
|
|||||||
@ -20,12 +20,12 @@
|
|||||||
;; i64->bytes on its own: two of its results cannot be held at once, and
|
;; i64->bytes on its own: two of its results cannot be held at once, and
|
||||||
;; these two numbers are both in the answer.
|
;; these two numbers are both in the answer.
|
||||||
(let [b (vec-new u8)]
|
(let [b (vec-new u8)]
|
||||||
(append! (addr b) (bytes "x="))
|
(append (addr b) (bytes "x="))
|
||||||
(append-i64! (addr b) 42)
|
(append-i64 (addr b) 42)
|
||||||
(append! (addr b) (bytes " y="))
|
(append (addr b) (bytes " y="))
|
||||||
(append-i64! (addr b) -7)
|
(append-i64 (addr b) -7)
|
||||||
(append! (addr b) (bytes " r="))
|
(append (addr b) (bytes " r="))
|
||||||
(append-f64! (addr b) 1.5)
|
(append-f64 (addr b) 1.5)
|
||||||
(show (addr b)) ; x=42 y=-7 r=1.5
|
(show (addr b)) ; x=42 y=-7 r=1.5
|
||||||
(free b))
|
(free b))
|
||||||
|
|
||||||
|
|||||||
@ -68,7 +68,7 @@
|
|||||||
;; trip is the only check that catches an encoder and a decoder that are
|
;; trip is the only check that catches an encoder and a decoder that are
|
||||||
;; wrong in the same direction — printing the bytes would not.
|
;; wrong in the same direction — printing the bytes would not.
|
||||||
(defn round-trip [code i32] i32
|
(defn round-trip [code i32] i32
|
||||||
(match (encode-rune! (slice scratch 0 4) code)
|
(match (encode-rune (slice scratch 0 4) code)
|
||||||
None -1
|
None -1
|
||||||
(Some w)
|
(Some w)
|
||||||
(let [r (decode-rune (slice scratch 0 w))]
|
(let [r (decode-rune (slice scratch 0 w))]
|
||||||
@ -82,7 +82,7 @@
|
|||||||
(let [it (split-on-byte s sep)
|
(let [it (split-on-byte s sep)
|
||||||
going true]
|
going true]
|
||||||
(while going
|
(while going
|
||||||
(match (split-next! (addr it))
|
(match (split-next (addr it))
|
||||||
(Some f) (do (print "[") (print f) (print "]"))
|
(Some f) (do (print "[") (print f) (print "]"))
|
||||||
None (set going false)))
|
None (set going false)))
|
||||||
(print " ")))
|
(print " ")))
|
||||||
@ -181,13 +181,13 @@
|
|||||||
(show-i32 (round-trip 0x10ffff))
|
(show-i32 (round-trip 0x10ffff))
|
||||||
(println "")
|
(println "")
|
||||||
|
|
||||||
;; Refused by encode-rune!, and nothing is written when it refuses.
|
;; 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) 0xd800)) ; -1, surrogate
|
||||||
(show-opt (encode-rune! (slice scratch 0 4) 0x110000)) ; -1, past the end
|
(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 4) -1)) ; -1, negative
|
||||||
(show-opt (encode-rune! (slice scratch 0 2) 0x65e5)) ; -1, buffer short
|
(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 0) 0x41)) ; -1, no room at all
|
||||||
(show-opt (encode-rune! (slice scratch 0 1) 0x41)) ; 1, exactly enough
|
(show-opt (encode-rune (slice scratch 0 1) 0x41)) ; 1, exactly enough
|
||||||
(println "")
|
(println "")
|
||||||
|
|
||||||
;; "Nothing is written when it refuses" is a claim about the buffer, not
|
;; "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
|
;; passes. So put a known byte in scratch, ask for an encoding that must be
|
||||||
;; refused, and read the byte back.
|
;; refused, and read the byte back.
|
||||||
(show-i32 (round-trip 0x41)) ; 65, scratch[0] = A
|
(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-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
|
(show-i32 (i32 (at scratch 0))) ; 65 still
|
||||||
(println "")
|
(println "")
|
||||||
|
|
||||||
@ -224,7 +224,7 @@
|
|||||||
total (i64 0)
|
total (i64 0)
|
||||||
going true]
|
going true]
|
||||||
(while going
|
(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)))
|
(Some f) (set total (+ total (match (parse-i64 (trim f)) (Some v) v None 0)))
|
||||||
None (set going false)))
|
None (set going false)))
|
||||||
(print total)
|
(print total)
|
||||||
|
|||||||
@ -797,7 +797,7 @@ let () =
|
|||||||
outputs "a number with a precision" "programs/format.flan" format_out;
|
outputs "a number with a precision" "programs/format.flan" format_out;
|
||||||
outputs ~opt:"-O0" "a number with a precision, -O0" "programs/format.flan"
|
outputs ~opt:"-O0" "a number with a precision, -O0" "programs/format.flan"
|
||||||
format_out;
|
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
|
the only sort in the language; these are copies rather than an
|
||||||
abstraction, because map/filter/reduce and a comparator sort all need a
|
abstraction, because map/filter/reduce and a comparator sort all need a
|
||||||
function value and check.ml refuses one outright.
|
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. *)
|
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
|
(* Removal is a map's operation and says so, rather than reaching for a
|
||||||
[len] that a Vec would also answer. *)
|
[len] that a Vec would also answer. *)
|
||||||
refuses_src "map-remove! wants a map"
|
refuses_src "map-remove wants a map"
|
||||||
"(defn main [] i32 (let [v (vec-new i32)] (map-remove! v 1) (free v)) 0)"
|
"(defn main [] i32 (let [v (vec-new i32)] (map-remove v 1) (free v)) 0)"
|
||||||
"map-remove! takes a (Map K V)";
|
"map-remove takes a (Map K V)";
|
||||||
refuses_src "a float is not a map key"
|
refuses_src "a float is not a map key"
|
||||||
"(defn f [m (Map f32 i32)] () 0)" "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"
|
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"
|
outputs ~dev:true "function values, dev" "programs/fn-values.flan"
|
||||||
fn_values_out;
|
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
|
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
|
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.
|
element type, which is the generics half. The f32 rows are that copy.
|
||||||
|
|||||||
@ -1925,13 +1925,13 @@ let () =
|
|||||||
|
|
||||||
let lines, _ =
|
let lines, _ =
|
||||||
with_config
|
with_config
|
||||||
{ Cimport.no_config with Cimport.renames = [ ("set_seed", "seed!") ] }
|
{ Cimport.no_config with Cimport.renames = [ ("set_seed", "seed") ] }
|
||||||
in
|
in
|
||||||
(* The C symbol is kept verbatim, so an override changes the Flan face and
|
(* 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
|
nothing else — which is what makes it safe to spell a predicate the way
|
||||||
Lisp spells one. *)
|
Lisp spells one. *)
|
||||||
check "a name override is the Flan name, and the C symbol is untouched"
|
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
|
(* 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
|
[spin-2d] by an accident of header order. Naming one of them is the way
|
||||||
@ -2696,9 +2696,9 @@ let () =
|
|||||||
write. *)
|
write. *)
|
||||||
rejects_check "a predicate is not carried through a generic call"
|
rejects_check "a predicate is not carried through a generic call"
|
||||||
~needle:"has to be carried by every signature"
|
~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"
|
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:
|
(* 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
|
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:
|
(* 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
|
it answers an (Option V), so the match around it still has to check while
|
||||||
the key is a variable. *)
|
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)} \
|
"(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"
|
accepts "and so do has-key?, reserve and clone"
|
||||||
"(defn f [m (Map $t i32) k $t] bool {:where (hashable? $t)} \
|
"(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))))";
|
(do (reserve m 8) (let [c (clone m)] (free c) (has-key? m k))))";
|
||||||
|
|||||||
@ -801,9 +801,9 @@ let () =
|
|||||||
|
|
||||||
(* 1. [C-c C-c] on a generic used to report [installs=false, fns=[]]: it
|
(* 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
|
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. *)
|
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 ->
|
| c ->
|
||||||
if not c.Session.installs then
|
if not c.Session.installs then
|
||||||
fail "redefining a generic installed nothing";
|
fail "redefining a generic installed nothing";
|
||||||
@ -812,28 +812,28 @@ let () =
|
|||||||
if not (List.mem want c.Session.fns) then
|
if not (List.mem want c.Session.fns) then
|
||||||
fail "redefining a generic did not install %s; it installed %s"
|
fail "redefining a generic did not install %s; it installed %s"
|
||||||
want (String.concat " " c.Session.fns))
|
want (String.concat " " c.Session.fns))
|
||||||
[ "hold!-i32"; "hold!-f64" ];
|
[ "hold-i32"; "hold-f64" ];
|
||||||
(* And only its own copies: [put!] did not change, and its copies are
|
(* 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
|
reached through their cells, so reinstalling them would be work with
|
||||||
no effect. *)
|
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"
|
fail "redefining a generic reinstalled an unchanged generic's copies"
|
||||||
| exception Loc.Error { Loc.dmsg = m; _ } ->
|
| exception Loc.Error { Loc.dmsg = m; _ } ->
|
||||||
fail "redefining a generic: %s" m);
|
fail "redefining a generic: %s" m);
|
||||||
|
|
||||||
(* The callee side of the same rule: redefining [put!] reinstalls the copies
|
(* The callee side of the same rule: redefining [put-at] reinstalls the copies
|
||||||
of [put!], which exist only because [hold!] asked for them — the
|
of [put-at], which exist only because [hold] asked for them — the
|
||||||
instantiation that generated them was transitive, and finding them again
|
instantiation that generated them was transitive, and finding them again
|
||||||
is one table lookup rather than a walk, because a whole-program check has
|
is one table lookup rather than a walk, because a whole-program check has
|
||||||
already regenerated all of them. *)
|
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 ->
|
| c ->
|
||||||
List.iter
|
List.iter
|
||||||
(fun want ->
|
(fun want ->
|
||||||
if not (List.mem want c.Session.fns) then
|
if not (List.mem want c.Session.fns) then
|
||||||
fail "redefining a called generic did not install %s; it \
|
fail "redefining a called generic did not install %s; it \
|
||||||
installed %s" want (String.concat " " c.Session.fns))
|
installed %s" want (String.concat " " c.Session.fns))
|
||||||
[ "put!-i32"; "put!-f64" ]
|
[ "put-at-i32"; "put-at-f64" ]
|
||||||
| exception Loc.Error { Loc.dmsg = m; _ } ->
|
| exception Loc.Error { Loc.dmsg = m; _ } ->
|
||||||
fail "redefining a generic: %s" 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
|
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
|
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
|
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
|
from i32 to i64 leaves [(put-at xs 0 v)] checking, because the literal
|
||||||
adapts, and changes [put!-i32]'s signature underneath every compiled
|
adapts, and changes [put-at-i32]'s signature underneath every compiled
|
||||||
caller. *)
|
caller. *)
|
||||||
(match
|
(match
|
||||||
Session.eval (gen ())
|
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))"
|
(set (at xs (i32 i)) v))"
|
||||||
with
|
with
|
||||||
| _ -> fail "a generic's changed parameter type was accepted"
|
| _ -> fail "a generic's changed parameter type was accepted"
|
||||||
| exception Loc.Error { Loc.dmsg = m; _ } ->
|
| exception Loc.Error { Loc.dmsg = m; _ } ->
|
||||||
if not (has m "changes signature") then
|
if not (has m "changes signature") then
|
||||||
fail "a generic's changed parameter type: %S" m;
|
fail "a generic's changed parameter type: %S" m;
|
||||||
if not (has m "the copy of the generic put!") then
|
if not (has m "the copy of the generic put-at") then
|
||||||
fail "the refusal did not say the name came from put!: %S" m;
|
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
|
if not (has m "every copy of it at once") then
|
||||||
fail "the refusal did not say every copy changed together: %S" m);
|
fail "the refusal did not say every copy changed together: %S" m);
|
||||||
|
|
||||||
|
|||||||
4
vendor/edn/read.flan
vendored
4
vendor/edn/read.flan
vendored
@ -87,7 +87,7 @@
|
|||||||
;; would be keeping a handle for an operation that never happens.
|
;; would be keeping a handle for an operation that never happens.
|
||||||
(defn copy-text [s [u8]] string
|
(defn copy-text [s [u8]] string
|
||||||
(let [b (vec-new u8)]
|
(let [b (vec-new u8)]
|
||||||
(append! (addr b) s)
|
(append (addr b) s)
|
||||||
(string (as-slice b))))
|
(string (as-slice b))))
|
||||||
|
|
||||||
;; ── Structural equality ─────────────────────────────────────────────
|
;; ── Structural equality ─────────────────────────────────────────────
|
||||||
@ -148,7 +148,7 @@
|
|||||||
(let [cur (i64 0)
|
(let [cur (i64 0)
|
||||||
k ""
|
k ""
|
||||||
v Value.Nil]
|
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)
|
(match (get b k)
|
||||||
(Some w) (when (not (value=? v w)) (return false))
|
(Some w) (when (not (value=? v w)) (return false))
|
||||||
None (return false))))
|
None (return false))))
|
||||||
|
|||||||
10
vendor/json/json.flan
vendored
10
vendor/json/json.flan
vendored
@ -465,7 +465,7 @@
|
|||||||
;; the last place that knows that offset — string-of runs over a literal this
|
;; 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
|
;; 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,
|
;; 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.
|
;; handed a code point the prelude refuses.
|
||||||
(defn read-string [c (Ptr Cursor) lo i32] Token
|
(defn read-string [c (Ptr Cursor) lo i32] Token
|
||||||
(let [s (.src c)
|
(let [s (.src c)
|
||||||
@ -500,7 +500,7 @@
|
|||||||
;; A high surrogate has to be followed by \u and a low one.
|
;; 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
|
;; A low one on its own, or a high one followed by anything
|
||||||
;; else, encodes no character: rune-size answers None for the
|
;; 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
|
;; the alternative to refusing here is a silently dropped
|
||||||
;; character later. That refusal is forced by the prelude
|
;; character later. That refusal is forced by the prelude
|
||||||
;; rather than chosen, and it is the reason the pairing rule
|
;; rather than chosen, and it is the reason the pairing rule
|
||||||
@ -703,11 +703,11 @@
|
|||||||
(set r (+ 0x10000
|
(set r (+ 0x10000
|
||||||
(bit-or (<< (- r 0xd800) 10) (- lo2 0xdc00))))))
|
(bit-or (<< (- r 0xd800) 10) (- lo2 0xdc00))))))
|
||||||
;; A four-byte buffer and not a push per byte, because
|
;; 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.
|
;; the shifts again here would be a second copy of UTF-8.
|
||||||
(let [buf (array 4 u8)]
|
(let [buf (array 4 u8)]
|
||||||
(match (encode-rune! (slice buf 0 4) r)
|
(match (encode-rune (slice buf 0 4) r)
|
||||||
(Some w) (append! (addr b) (slice buf 0 w))
|
(Some w) (append (addr b) (slice buf 0 w))
|
||||||
;; Unreachable: read-string refuses every code point
|
;; Unreachable: read-string refuses every code point
|
||||||
;; rune-size refuses. Written as a no-op rather than a trap
|
;; rune-size refuses. Written as a no-op rather than a trap
|
||||||
;; because a dropped character is not worth a crash and the
|
;; because a dropped character is not worth a crash and the
|
||||||
|
|||||||
@ -941,7 +941,7 @@ are four predicates, and each gates builtins the compiler already has:</p>
|
|||||||
|
|
||||||
<p>They entail each other in one direction, so one clause usually does:
|
<p>They entail each other in one direction, so one clause usually does:
|
||||||
<code>numeric?</code> gives <code>ordered?</code>, and <code>ordered?</code> gives
|
<code>numeric?</code> gives <code>ordered?</code>, and <code>ordered?</code> gives
|
||||||
<code>equal?</code>. A <code>sort!</code> that compares its elements declares
|
<code>equal?</code>. A <code>sort</code> that compares its elements declares
|
||||||
<code>ordered?</code> and nothing else.</p>
|
<code>ordered?</code> and nothing else.</p>
|
||||||
|
|
||||||
<p><strong>Every value copies.</strong> There used to be a fifth predicate,
|
<p><strong>Every value copies.</strong> There used to be a fifth predicate,
|
||||||
@ -1044,13 +1044,13 @@ over.</p>
|
|||||||
<div class="scroll">
|
<div class="scroll">
|
||||||
<table>
|
<table>
|
||||||
<tr><th>Group</th><th>Names</th></tr>
|
<tr><th>Group</th><th>Names</th></tr>
|
||||||
<tr><td>slice algorithms, over one type variable</td><td><code>swap!</code>, <code>reverse!</code>, <code>sort!</code>, <code>sort-by!</code>, <code>index-of</code>, <code>min-of</code>, <code>max-of</code>, <code>map!</code>, <code>reduce</code>, <code>filter</code></td></tr>
|
<tr><td>slice algorithms, over one type variable</td><td><code>swap</code>, <code>reverse</code>, <code>sort</code>, <code>sort-by</code>, <code>index-of</code>, <code>min-of</code>, <code>max-of</code>, <code>map-in-place</code>, <code>reduce</code>, <code>filter</code></td></tr>
|
||||||
<tr><td>the per-type layer that stays</td><td><code>sum-i32</code>, <code>sum-f32</code> — the element and the accumulator are different types, which one variable cannot say</td></tr>
|
<tr><td>the per-type layer that stays</td><td><code>sum-i32</code>, <code>sum-f32</code> — the element and the accumulator are different types, which one variable cannot say</td></tr>
|
||||||
<tr><td>bytes</td><td><code>bytes=?</code>, <code>bytes<?</code>, <code>bytes-ci=?</code>, <code>starts-with?</code>, <code>ends-with?</code>, <code>index-of-bytes</code>, <code>trim</code>, <code>digit?</code>, <code>space?</code>, <code>sort-bytes!</code></td></tr>
|
<tr><td>bytes</td><td><code>bytes=?</code>, <code>bytes<?</code>, <code>bytes-ci=?</code>, <code>starts-with?</code>, <code>ends-with?</code>, <code>index-of-bytes</code>, <code>trim</code>, <code>digit?</code>, <code>space?</code>, <code>sort-bytes</code></td></tr>
|
||||||
<tr><td>parsing</td><td><code>parse-i64</code>, <code>parse-f64</code></td></tr>
|
<tr><td>parsing</td><td><code>parse-i64</code>, <code>parse-f64</code></td></tr>
|
||||||
<tr><td>text</td><td><code>split-on-byte</code>, <code>split-next!</code>, <code>split</code>, <code>lower-ascii</code>, <code>upper-ascii</code>, <code>to-lower</code>, <code>to-upper</code></td></tr>
|
<tr><td>text</td><td><code>split-on-byte</code>, <code>split-next</code>, <code>split</code>, <code>lower-ascii</code>, <code>upper-ascii</code>, <code>to-lower</code>, <code>to-upper</code></td></tr>
|
||||||
<tr><td>building bytes</td><td><code>append!</code>, <code>append-i64!</code>, <code>append-f64!</code>, <code>concat</code>, <code>join</code>, <code>repeat-bytes</code>, <code>replace-bytes</code>, <code>slices-new</code>, <code>format-f64</code></td></tr>
|
<tr><td>building bytes</td><td><code>append</code>, <code>append-i64</code>, <code>append-f64</code>, <code>concat</code>, <code>join</code>, <code>repeat-bytes</code>, <code>replace-bytes</code>, <code>slices-new</code>, <code>format-f64</code></td></tr>
|
||||||
<tr><td>UTF-8</td><td><code>decode-rune</code>, <code>rune-at</code>, <code>rune-count</code>, <code>rune-size</code>, <code>rune-start?</code>, <code>valid-utf8?</code>, <code>encode-rune!</code></td></tr>
|
<tr><td>UTF-8</td><td><code>decode-rune</code>, <code>rune-at</code>, <code>rune-count</code>, <code>rune-size</code>, <code>rune-start?</code>, <code>valid-utf8?</code>, <code>encode-rune</code></td></tr>
|
||||||
<tr><td>numbers</td><td><code>sign-f32</code>, <code>lerp</code>, <code>clamp</code>, <code>floor-f32</code>, <code>ceil-f32</code>, <code>round-f32</code>, <code>abs-i32</code>, <code>abs-i64</code>, the constants <code>pi-f32</code>, <code>pi-f64</code>, <code>tau-f32</code>, <code>tau-f64</code>, and libm through a <code>declare</code> at both widths: <code>sqrt</code>, <code>abs</code>, <code>floor</code>, <code>ceil</code>, <code>round</code>, <code>fmod</code>, <code>sin</code>, <code>cos</code>, <code>tan</code>, <code>asin</code>, <code>acos</code>, <code>atan</code>, <code>atan2</code>, <code>log</code>, <code>log2</code>, <code>log10</code>, <code>exp</code>, <code>pow</code>, <code>hypot</code>, <code>cbrt</code> — each spelled <code>-f32</code> or <code>-f64</code></td></tr>
|
<tr><td>numbers</td><td><code>sign-f32</code>, <code>lerp</code>, <code>clamp</code>, <code>floor-f32</code>, <code>ceil-f32</code>, <code>round-f32</code>, <code>abs-i32</code>, <code>abs-i64</code>, the constants <code>pi-f32</code>, <code>pi-f64</code>, <code>tau-f32</code>, <code>tau-f64</code>, and libm through a <code>declare</code> at both widths: <code>sqrt</code>, <code>abs</code>, <code>floor</code>, <code>ceil</code>, <code>round</code>, <code>fmod</code>, <code>sin</code>, <code>cos</code>, <code>tan</code>, <code>asin</code>, <code>acos</code>, <code>atan</code>, <code>atan2</code>, <code>log</code>, <code>log2</code>, <code>log10</code>, <code>exp</code>, <code>pow</code>, <code>hypot</code>, <code>cbrt</code> — each spelled <code>-f32</code> or <code>-f64</code></td></tr>
|
||||||
<tr><td>time</td><td><code>monotonic-ns</code>, <code>monotonic-seconds</code>, <code>unix-ns</code>, <code>unix-seconds</code>, <code>sleep-ns</code>, <code>sleep-seconds</code>, and <code>ns-per-second</code> and its two smaller siblings</td></tr>
|
<tr><td>time</td><td><code>monotonic-ns</code>, <code>monotonic-seconds</code>, <code>unix-ns</code>, <code>unix-seconds</code>, <code>sleep-ns</code>, <code>sleep-seconds</code>, and <code>ns-per-second</code> and its two smaller siblings</td></tr>
|
||||||
<tr><td>files</td><td><code>file-exists?</code> and <code>file-size</code>, which answer a value; <code>slurp</code>, <code>barf</code>, <code>delete-file</code>, <code>rename-file</code> and <code>make-directory</code>, which signal <code>FileError</code> under <code>retry</code> and <code>use-value</code></td></tr>
|
<tr><td>files</td><td><code>file-exists?</code> and <code>file-size</code>, which answer a value; <code>slurp</code>, <code>barf</code>, <code>delete-file</code>, <code>rename-file</code> and <code>make-directory</code>, which signal <code>FileError</code> under <code>retry</code> and <code>use-value</code></td></tr>
|
||||||
@ -1062,8 +1062,8 @@ over.</p>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p><strong>One family, not one per type.</strong> The slice algorithms used to be
|
<p><strong>One family, not one per type.</strong> The slice algorithms used to be
|
||||||
<code>sort-i32!</code> beside <code>sort-f32!</code> beside <code>sort-bytes!</code>, and
|
<code>sort-i32</code> beside <code>sort-f32</code> beside <code>sort-bytes</code>, and
|
||||||
generics collapsed them: <code>sort!</code> is written once and instantiated at whatever
|
generics collapsed them: <code>sort</code> is written once and instantiated at whatever
|
||||||
element type the call passes. <code>sum-i32</code> and <code>sum-f32</code> are what did
|
element type the call passes. <code>sum-i32</code> and <code>sum-f32</code> are what did
|
||||||
<em>not</em> collapse, and they are the honest exception — each widens its element into a
|
<em>not</em> collapse, and they are the honest exception — each widens its element into a
|
||||||
different accumulator, which one variable cannot express.</p>
|
different accumulator, which one variable cannot express.</p>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user