;;;; (Vec T) — spec-memory.md, "The four container types" and "Allocators". ;;;; ;;;; ptr + len + cap + allocator, owning and move-only, over one type-erased ;;;; runtime. The element type appears nowhere in that runtime: size_of and ;;;; align_of are produced at the call site, which without generics is simply ;;;; the concrete call site. So this file being two element types with one ;;;; runtime behind them is the whole claim. (defstruct Point [x i32 y i32]) ;;; Ownership transfers on the call. The caller's binding is dead after this, ;;; which is what the refusal cases in test_acceptance assert. (defn consume [v (Vec i32)] i32 (let [n (len v)] (free v) n)) ;;; A Vec is returned by moving it out, so the callee's binding is the ;;; caller's. Nothing is released at function exit — there is no scope-end ;;; anything in this language. (defn make [n i32] (Vec i32) (let [v (vec-new i32)] (dotimes [i n] (push v (* i i))) v)) (defn sum [xs [i32]] i32 (let [total 0] (dotimes [i (len xs)] (set total (+ total (at xs i)))) total)) (defn main [] i32 (let [v (vec-new i32)] (println (len v)) ; 0 (push v 10) (push v 20) (push v 30) (println (len v)) ; 3 (println (at v 0)) ; 10 (println (at v 2)) ; 30 ;; A Vec element is a place, and the same bounds and epoch check stands ;; behind the value form and the place form. (set (at v 1) 99) (println (at v 1)) ; 99 ;; as-slice is a non-owning view: it copies ptr+len and never the ;; elements, and it carries no allocator, so nothing can be freed through ;; one. [at] and [len] over it are the array operations, unchanged. (println (sum (as-slice v))) ; 139 (println (len (as-slice v 1 3))) ; 2 (println (at (as-slice v 1 3) 0)) ; 99 ;; clone is the only copy: assignment moves. The copy is independent, and ;; freeing it leaves the original alone. (let [w (clone v)] (set (at w 0) -1) (println (at w 0)) ; -1 (println (at v 0)) ; 10 (free w)) ;; reserve does not change the length, only the capacity, so a reserve ;; that succeeds is invisible except that the pushes after it do not grow. (reserve v 64) (println (len v)) ; 3 (push v 40) (println (len v)) ; 4 ;; The structural printer reaches both new types. Neither is followed: a ;; Vec's elements are printed through (as-slice v), which says at the call ;; site that it borrowed, and an allocator's contents are the runtime's and ;; its address is not stable across runs. (println v) ; (println context/allocator) ; (free v)) ;; A second element type over the same runtime, and a struct element, so ;; that size_of and align_of are doing work rather than both being 4. (let [ps (vec-new Point)] (push ps (Point {.x 1 .y 2})) (push ps (Point {.x 3 .y 4})) (println (len ps)) ; 2 (println (.y (at ps 1))) ; 4 (free ps)) ;; A Vec made against an explicit allocator records it, so free and clone ;; never need it named again. An arena cannot free one block, so this free ;; keeps the block — releasing it is free-all's job, and that is the ;; difference the capability set exists to state. (let [a (arena-new 4096)] (let [v (vec-new i32 a)] (push v 7) (println (at v 0)) ; 7 (free v)) (println (can-free? a)) ; false (free-all a) (arena-destroy a)) ;; The pushes go into whatever the context names, with nothing passed. (let [a (arena-new 4096)] (with-allocator a (let [v (vec-new i32)] (push v 5) (push v 6) (println (+ (at v 0) (at v 1))) ; 11 (free v))) (arena-destroy a)) (println (consume (make 5))) ; 5 0)