map-keys and map-values are prelude generics over any hashable key
This commit is contained in:
parent
52c154e306
commit
6745aa7cb4
5
TODO.org
5
TODO.org
@ -916,11 +916,6 @@ Refused by name, narrower than the spec's key set; a struct holding the array
|
|||||||
works. It needs the per-element walk a struct key gets, driven by a loop rather
|
works. It needs the per-element walk a struct key gets, driven by a loop rather
|
||||||
than a field list.
|
than a field list.
|
||||||
|
|
||||||
** TODO map-keys and map-values cannot be prelude functions
|
|
||||||
Iteration is built; the remaining refusal is generics. A =defn= has to name its
|
|
||||||
types and =(defn map-keys [m (Map K V)] (Vec K))= has no =K=. The loop is three
|
|
||||||
lines at the call site, where =K= is known.
|
|
||||||
|
|
||||||
** DONE (vec-new [u8]) is refused
|
** DONE (vec-new [u8]) is refused
|
||||||
CLOSED: [2026-09-25]
|
CLOSED: [2026-09-25]
|
||||||
The type positions of =vec-new= and =map-new= take a type expression: brackets, or
|
The type positions of =vec-new= and =map-new= take a type expression: brackets, or
|
||||||
|
|||||||
@ -9434,9 +9434,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
|||||||
(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
|
The prelude's map-keys and map-values are this loop over a generic key.
|
||||||
K, and a prelude defn cannot be written at every K. That one is generics,
|
|
||||||
not iteration, and it stays refused for that reason.
|
|
||||||
|
|
||||||
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,
|
||||||
|
|||||||
@ -564,6 +564,30 @@ let source = {flan|
|
|||||||
(push v (at s i))))
|
(push v (at s i))))
|
||||||
v))
|
v))
|
||||||
|
|
||||||
|
;; A map's keys, and its values, as a new Vec the caller owns. In block order,
|
||||||
|
;; which is the hash's and not the insertion's — sort what comes back if the
|
||||||
|
;; order matters. A string key is copied as the view it is, so the Vec reads
|
||||||
|
;; the map's own key bytes and is good for as long as they are.
|
||||||
|
(defn map-keys [m (Map $k $v)] (Vec $k)
|
||||||
|
{:where (hashable? $k)}
|
||||||
|
(let [out (vec-new k)
|
||||||
|
cur (i64 0)
|
||||||
|
key (the $k (zeroed))
|
||||||
|
val (the $v (zeroed))]
|
||||||
|
(while (map-next m (addr cur) (addr key) (addr val))
|
||||||
|
(push out key))
|
||||||
|
out))
|
||||||
|
|
||||||
|
(defn map-values [m (Map $k $v)] (Vec $v)
|
||||||
|
{:where (hashable? $k)}
|
||||||
|
(let [out (vec-new v)
|
||||||
|
cur (i64 0)
|
||||||
|
key (the $k (zeroed))
|
||||||
|
val (the $v (zeroed))]
|
||||||
|
(while (map-next m (addr cur) (addr key) (addr val))
|
||||||
|
(push out val))
|
||||||
|
out))
|
||||||
|
|
||||||
;; ── The sign questions, over every numeric type at once ───────────────
|
;; ── The sign questions, over every numeric type at once ───────────────
|
||||||
;;
|
;;
|
||||||
;; The family the whole of generics was asked for. Three questions about a
|
;; The family the whole of generics was asked for. Three questions about a
|
||||||
@ -1925,15 +1949,6 @@ let source = {flan|
|
|||||||
;; that did not come with them, because it is one copy
|
;; that did not come with them, because it is one copy
|
||||||
;; per *ordered pair* of types rather than per type,
|
;; per *ordered pair* of types rather than per type,
|
||||||
;; 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
|
|
||||||
;; point of naming them separately. It used to be the
|
|
||||||
;; missing Map iterator; `map-next` is that iterator
|
|
||||||
;; and walking a map is expressible now. What a defn
|
|
||||||
;; still cannot say is (defn map-keys [m {K V}] (Vec K)):
|
|
||||||
;; a prelude function has to name its types, and there
|
|
||||||
;; is no K. The loop is three lines at the call site,
|
|
||||||
;; where K is known, and that is where it stays until
|
|
||||||
;; there are generics.
|
|
||||||
;;
|
;;
|
||||||
;; Builder Not refused — declined. strings.Builder in Odin
|
;; Builder Not refused — declined. strings.Builder in Odin
|
||||||
;; wraps a [dynamic]u8; here the (Vec u8) *is* that and
|
;; wraps a [dynamic]u8; here the (Vec u8) *is* that and
|
||||||
|
|||||||
34
test/programs/map-keys.flan
Normal file
34
test/programs/map-keys.flan
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
;; map-keys and map-values: the prelude's two generic walks over a map. Block
|
||||||
|
;; order is the hash's, so what comes back is sorted before it is printed.
|
||||||
|
|
||||||
|
(defn main [] i32
|
||||||
|
(let [m (map-new i32 i64)]
|
||||||
|
(put m 30 (i64 300))
|
||||||
|
(put m 10 (i64 100))
|
||||||
|
(put m 20 (i64 200))
|
||||||
|
(let [ks (map-keys m)
|
||||||
|
vs (map-values m)]
|
||||||
|
(sort (slice ks))
|
||||||
|
(sort (slice vs))
|
||||||
|
(dotimes [i (length ks)] (print (at ks i)) (print " "))
|
||||||
|
(println "")
|
||||||
|
(dotimes [i (length vs)] (print (at vs i)) (print " "))
|
||||||
|
(println "")
|
||||||
|
(free ks)
|
||||||
|
(free vs)))
|
||||||
|
;; A string key, and a map that never allocated.
|
||||||
|
(let [names (map-new string i32)
|
||||||
|
none (map-new string i32)]
|
||||||
|
(put names "b" 2)
|
||||||
|
(put names "a" 1)
|
||||||
|
(let [ks (map-keys names)
|
||||||
|
nk (map-keys none)
|
||||||
|
total 0]
|
||||||
|
(dotimes [i (length ks)] (set total (+ total (or-else (get names (at ks i)) 0))))
|
||||||
|
(println total)
|
||||||
|
(println (length nk))
|
||||||
|
(free ks)
|
||||||
|
(free nk))
|
||||||
|
(free names)
|
||||||
|
(free none))
|
||||||
|
0)
|
||||||
@ -4422,6 +4422,8 @@ level "1"
|
|||||||
in
|
in
|
||||||
outputs "map iteration" "programs/map-iter.flan" map_iter_out;
|
outputs "map iteration" "programs/map-iter.flan" map_iter_out;
|
||||||
outputs ~opt:"-O0" "map iteration, -O0" "programs/map-iter.flan" map_iter_out;
|
outputs ~opt:"-O0" "map iteration, -O0" "programs/map-iter.flan" map_iter_out;
|
||||||
|
outputs "map-keys and map-values" "programs/map-keys.flan"
|
||||||
|
"10 20 30 \n100 200 300 \n3\n0\n";
|
||||||
|
|
||||||
(* Removal, which is the operation that can break the others. A probe
|
(* Removal, which is the operation that can break the others. A probe
|
||||||
stops at the first group holding an empty slot, so a slot emptied in
|
stops at the first group holding an empty slot, so a slot emptied in
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user