No matching definitions.

tur/vec

stdlib/vec.tur

typed Vec[A]: parameterized growable array.

Parameterized growable array. Runtime layout identical to tur/vec.

Since: Phase TC1-A

defstruct

Vec

(defstruct Vec :heap [A] (data :ptr<void>) (len :int) (cap :int))

parameterized growable array with element type A.

Since: Phase TC1

defn

vec-new

(vec-new [A])

allocate a new empty Vec[A].

A new empty Vec[A] with len=0 and cap=0.

(vec-new)  ; => empty Vec

Since: Phase TC1

defn

vec-len

(vec-len [A])

return the number of elements in a Vec[A].

#reads v is the trusted claim that this body only READS the vector it is handed. It lets a refinement over (vec-len v) be treated as a function of frozen state inside a frozen region, so a bounded-index loop invariant can discharge. A mutator (vec-push!, vec-set-o!, ...) deliberately carries no such claim -- that asymmetry is how the frozen grant tells a reader from a writer, since the SIGNATURE cannot: both take the vector by value. See docs/guides/stateful-refinements-guide.md.

vVec[A] pointer

Element count.

(vec-len my-vec)  ; => 3

Since: Phase TC1

defn

vec-get

(vec-get [A])

return the element at index i (bounds-checked).

#reads v marks this a reader -- see the note on vec-len.

vVec[A] pointer
izero-based index

The element at index i (typed A, stored as int64_t).

(vec-get my-vec 0)  ; => first element

Since: Phase TC1

defn

vec-push!

(vec-push! [A])

append an element to the Vec[A].

vVec[A] pointer
valelement of type A to append
(vec-push! my-vec 42)

Since: Phase TC1

defn

vec-pop!

(vec-pop! [A])

remove and return the last element of a Vec[A].

Ownership note for MULTI-WORD (> 8 byte by-value struct/ADT) elements: such an element rides its data slot as a heap box pointer. vec-pop! returns that box pointer on the carrier and shrinks len, so ownership of the box TRANSFERS OUT to the caller -- vec-free will no longer see it. The usual read pattern (:: (vec-pop! v) T) deref-copies the box to a value and then drops the box, which leaks it (there is no handle left to free). To REMOVE the last element without keeping it, use vec-drop-last! (which frees the box); to look at it, vec-get before popping. Single-word A (int/cstr/float/opaque/:heap ptr) has no box, so vec-pop! is leak-free there.

vVec[A] pointer

The last element of type A (as int64_t), or 0 if empty.

(vec-pop! my-vec)  ; => last element

Since: Phase TC1

defn

vec-drop-last-o!

(vec-drop-last-o! [A])

remove the last element and (when boxed) free its box.

The removal twin of vec-pop! that keeps NOTHING: it shrinks len and, when bit 0 of owned is set (a wide by-value element whose slot is an owned heap box), frees that box so it does not leak; when bit 1 is set (an rc<T> element) it releases the slot's strong reference. Unlike vec-pop! it returns no value, so there is no ownership-transfer hazard. Internal helper for the vec-drop-last! macro; not called directly.

vVec[A] pointer
ownedbit 0: the last slot is an owned element box; bit 1: the
last slot holds a strong rc<T> reference the Vec owns

Since: multiword-element-box-lifecycle

defmacro

vec-drop-last!

(vec-drop-last! [v])

remove the last element of a Vec[A], freeing its box.

A macro so the multi-word-element decision folds at emit time: it binds v once and forwards it to vec-drop-last-o! along with (tur-vec-elem-wide? v). For a wide by-value element the removed slot's heap box is freed; for a single-word element only len shrinks. Use this instead of a discarded (vec-pop! v) when you want to drop the last element without keeping it -- vec-pop! would transfer the box out and leak it (see vec-pop!).

vVec[A] pointer
(vec-drop-last! my-vec)

Since: multiword-element-box-lifecycle

defn

vec-set-o!

(vec-set-o! [A])

overwrite the element at index i, freeing the old box when boxed.

Internal helper for the vec-set! macro. A multi-word by-value element rides its data slot as an owned heap box; overwriting the slot orphans the old box, so when bit 0 of owned is set the old data[i] box is freed before the new value is stored. When bit 1 is set (an rc<T> element) the old slot's strong reference is released instead. When owned is 0 (single-word, non-rc A) the slot is an inline value / shared pointer the Vec does not own, so it is overwritten directly.

Aliasing caveat: freeing the old box invalidates any raw carrier a caller captured from an earlier (vec-get v i). The normal read pattern (:: (vec-get v i) T) deref-copies immediately, so no live alias survives the overwrite -- this holds under the collection's value-semantics ownership model (the Vec owns its element boxes).

vVec[A] pointer
izero-based index
valnew element value of type A
ownedbit 0: the slot is an owned element box; bit 1: the slot holds
a strong rc<T> reference the Vec owns

Since: multiword-element-box-lifecycle

defmacro

vec-set!

(vec-set! [v i val])

overwrite the element at index i (freeing any old element box).

A macro so the multi-word-element decision folds at emit time: it binds v/i/ val once (preserving left-to-right evaluation) and forwards them to vec-set-o! along with (tur-vec-elem-wide? v). For a wide by-value element the overwritten slot's old heap box is freed; for a single-word element the slot is overwritten directly. See vec-set-o! for the aliasing caveat.

vVec[A] pointer
izero-based index
valnew element value of type A
(vec-set! my-vec 0 99)

Since: Phase TC1

defn

tur-vec-elem-wide?

(tur-vec-elem-wide? [A])

1 if the Vec's element type A is a multi-word (> 8 byte) by-value struct/ADT that rides its data slot as an int64 heap-box pointer, else 0. Compiler-intercepted (src/compiler/emit_expr.c) as an EMIT-TIME type query that peels A out of the (Vec A) spine and reuses the same type_is_wide_byval_adt predicate the element-boxing bridge keys on; this pure-Turmeric fallback body returns 0 for the interpreter (where an element rides as a TuriStruct pointer, never a C box) and for any call the backend does not intercept. vec-free threads it to vec-free-o so a Vec[Point]-style boxed buffer releases its element boxes on free.

Since: multiword-element-box-lifecycle

defn

tur-vec-elem-rc?

(tur-vec-elem-rc? [A])

1 if the Vec's element type A is an rc<T>, whose data slots hold a strong reference the Vec owns, else 0. Compiler-intercepted (src/compiler/emit_expr.c) as an EMIT-TIME type query, exactly like its sibling tur-vec-elem-wide?; this pure-Turmeric fallback body returns 0 for the interpreter (where an rc rides as a first-class value, never a carrier-erased control-block pointer) and for any call the backend does not intercept.

vec-free / vec-set! / vec-drop-last! thread it as BIT 1 of the owned flag (bit 0 being tur-vec-elem-wide?), so a Vec[rc<T>] releases each element's strong reference at the same points a Vec[Point] frees each element box. The matching increment is stamped at the push site by elab (own_carry_for_arg in src/compiler/elab_call.c).

Since: collections-cannot-hold-rc-values

defn

vec-free-o

(vec-free-o [A])

free a Vec[A], its data buffer, and (when boxed) each per-element heap box.

A multi-word by-value element A cannot ride the int64 data slot inline, so the concrete->carrier escaping bridge boxes it (malloc + copy) at the push site and stores the box pointer in data[i]. Vec is mutable and NOT structurally shared, so the Vec owns those boxes outright: when bit 0 of owned is set (threaded by vec-free via the emit-time tur-vec-elem-wide? query) each data[i] box is freed before the data buffer.

Bit 1 (threaded from tur-vec-elem-rc?) is the refcount twin: for a Vec[rc<T>] each slot holds a strong reference taken at the push site, so every one is released before the buffer goes. When owned is 0 (any single-word, non-rc A -- :int, :cstr, :float, an opaque handle, a :heap pointer) the slot holds an inline value / shared pointer that the Vec does not own, so only the data buffer and header are freed.

vVec[A] pointer to free
ownedbit 0: each data slot is an owned element box; bit 1: each
data slot holds a strong rc<T> reference the Vec owns

Since: multiword-element-box-lifecycle

defmacro

vec-free

(vec-free [v])

free a Vec[A], its data buffer, and any per-element boxes.

A macro (not a plain defn) so the multi-word-element decision folds at emit time: it binds v once and forwards it to vec-free-o along with (tur-vec-elem-wide? v), which the compiler resolves per monomorphization to 1 for a wide by-value element type (whose data slots are owned heap boxes) and 0 otherwise. The interpreter expands the same form; there tur-vec-elem-wide? is 0 and vec-free-o routes to native_vec_free_o (the per-element boxes do not exist in the tree-walker).

vVec[A] pointer to free
(vec-free my-vec)

Since: Phase TC1

defmacro

vec-push-each__

(vec-push-each__ [v & xs])

internal helper for the vec-of macro.

Expands a sequence of pushes onto v followed by v itself as the final value. Recursive: each step emits (do (vec-push! v x) ...).

defn

tur-vec-homog__

(tur-vec-homog__ [A])

internal: compile-time element homogeneity check.

Both parameters share the single type variable A, so a call with two differently-typed arguments is a TUR-E0001 at elaboration. The body is a no-op; the call exists only to make the type-checker enforce that adjacent vec-of elements have the same type. No allocation (fixed arity).

defmacro

vec-homog-chain__

(vec-homog-chain__ [& xs])

internal: pairwise-chain tur-vec-homog__ over xs.

Emits (tur-vec-homog__ x0 x1), (tur-vec-homog__ x1 x2), ... so all elements transitively unify to one type. Zero or one element: no check.

defn

vec-empty-like__

(vec-empty-like__ [A])

internal: a fresh empty Vec[A] whose element type A is grounded by a type-witness value.

(vec-new) on its own returns a bare :A result that summarizes to the int64 carrier, so a (let [v (vec-new)] ...) binding freezes at the carrier and later element reads (show/eq? dispatch) cannot recover the concrete element type -- a Vec[cstr] then shows/compares by pointer. By threading one already-typed element through witness : A, the returned (:: (vec-new) (Vec A)) is a genuine (Vec A) whose head carries the concrete element type, so the vec-of result dispatches element typeclass methods correctly. The witness value itself is unused (only its type is consumed), so it never widens or truncates the stored data.

defmacro

vec-of

(vec-of [& xs])

variadic Vec[A] literal.

& xs -- zero or more elements, all of type A.

A fresh Vec[A] populated with xs in order. The element type A may be any single-word type (:int, :cstr, :bool, :nil, :float, an opaque handle, or a heap struct/ADT pointer); elements are stored through an :int carrier slot, so a :float element round-trips as a bit-reinterpret. The result's static type is a genuine (Vec A) -- its head carries the concrete element type -- so (show (vec-of "a" "b")) renders "[a b]" and (eq? (vec-of "a") (vec-of "b")) compares by the element's Eq instance rather than by carrier identity. The handle composes with vec-len, vec-get, vec-push!, etc.; vec-get returns :A, so a non-int element is read with an ascription, e.g. (:: (vec-get v 0) :float).

(vec-of)        ; => empty Vec
(vec-of 1 2 3)  ; => Vec containing 1, 2, 3
[1 2 3]         ; => same, with -Xdata-literals

Note: all elements must unify to a single type. Mixing types -- e.g.

(vec-of 1 "x" 3.14) -- is a type error (TUR-E0001 on the offending
element). For heterogeneous fixed-arity collections, use tupleN.
Aggregate (multi-word struct/ADT) element types are not supported by
the carrier slot; those need a per-element typed layout (see TS3b).

Note: each element expression is evaluated exactly once (the first is

bound once and reused as both the type witness and the first push).

Note: under -Xdata-literals, a [...] vector in expression position lowers

to this macro, so [1 2 3] is sugar for (vec-of 1 2 3).  See
docs/guides/data-literals-guide.md.

Since: Phase 2 cons-in-docs cleanup

defn

vec-eq?

(vec-eq? [A])

compare two Vec[A] element-wise using a comparator.

This helper hands the comparator RAW slot words -- it reads a->data[i] straight out of the buffer rather than passing a value across the carrier boundary. So do list-eq?, option-eq?, result-eq? and pair-eq?, each from its own payload slots; map-eq? and set-eq-cmp? are the exception, handing over a HAMT-stored carrier box instead. The difference matters only for an element whose word is not a box, a niche (Option P) above all: it rides its slot as the payload pointer itself.

Either spelling works, here and at every one of those helpers: declare the comparator's parameter types, or write the comparator inline at the call, and the value is bridged for you whichever convention it arrived in. A NAMED comparator with erased parameters over such an element cannot be given the convention and is refused with TUR-E0715.

v1first Vec[A]
v2second Vec[A]
cmp-fncomparator fn [a :A b :A] :bool, receiving raw slot words

true if both vecs have the same length and all element pairs satisfy cmp-fn.

(vec-eq? va vb (fn [a b] (= a b)))  ; => true
(vec-eq? vo wo (fn [a : (Option String) b : (Option String)] : bool
                 (eq? a b)))        ; => true

Since: Phase TC1

defn

vec-data-get__

(vec-data-get__ [data :ptr<void> i :int] :int)

element-wise equality using the element type's Eq instance.

defn

vec-data-get-checked__

(vec-data-get-checked__ [data :ptr<void> i :int len :int] :int)

bounds-checked indexed read over a raw data ptr.

defn

vec-get-byval

(vec-get-byval [A])

by-value Vec[A] indexed read (Option C twin of vec-get).

defn

vec-eq-loop

(vec-eq-loop [^Eq A])

pure-Turmeric element-wise Vec[A] comparison (TCO'd index loop).

Path A specializes this per element type; the self-tail-call lowers to a goto loop inside the spec (TCO-in-ABI-specs, emit_fns.c eligibility gate), so deep iteration on large vecs does not grow the C stack. Unlike the inline-C vec-eq? carrier helper, the receiver flows by value: vec-get is redirected to vec-get-byval inside the spec body, so no (:: x :int) relabel and no concrete->carrier bridge is minted at the Eq [Vec] dispatch site.

The ^Eq A constraint scopes the element Eq dictionary and each pair is compared with (eq? (:: (vec-get x i) A) (:: (vec-get y i) A)) directly -- mirroring vec-show-loop's element dispatch. This is deliberately NOT a comparator-closure design: an (fn [a b] (eq? a b)) lambda passed in from the Eq[Vec] instance defaults its params to the int64 carrier and bakes a single non-specialized Eq[int] closure (pointer/word comparison), which is a silent miscompare for cstr and every other content type. Reading and ascribing the element to A here re-drives dispatch per ABI specialization, so Eq[cstr] (strcmp), Eq[bool], etc. are threaded correctly.

xfirst Vec[A]
ysecond Vec[A]
icurrent index (start at 0)
lenshared length (both vecs are known equal-length by the caller)

true if x[i..len) equals y[i..len) under Eq[A].

(vec-eq-loop a b 0 (vec-len a))  ; => true

Since: Phase M4c (TCO-in-ABI-specs)

definstance

Eq[Vec]

(definstance Eq [Vec])

element-wise equality using the element type's Eq instance.