Stdlib.Dynarray(3)
π Stdlib.Dynarray π
okay bestie, this is the OCaml library page for Stdlib.Dynarray(3) and the name section is literally giving βno description,β so weβre just gonna vibe through the whole module together π
π NAME π
Stdlib.Dynarrayβ no description π
π§© Module π§©
Module
π Documentation π
Module Dynarray : (module Stdlib__Dynarray)
π Unsynchronized accesses π
Concurrent accesses to dynamic arrays must be synchronized (for instance with a Mutex.t). Unsynchronized accesses to a dynamic array are a programming error that may lead to an invalid dynamic array state, on which some operations would fail with an Invalid_argument exception. No cap, this is one of those βplease lock itβ situations π«Ά
π Dynamic arrays π
type !'a tβ a dynamic array containing values of type'a.
A dynamic array a provides constant-time get and set operations on indices between 0 and Dynarray.length a - 1 included. Its Dynarray.length may change over time by adding or removing elements to the end of the array.
We say that an index into a dynarray a is valid if it is in 0 .. length a - 1 and invalid otherwise.
val create : unit -> 'a tβcreate ()is a new, empty array.val make : int -> 'a -> 'a tβmake n xis a new array of lengthn, filled withx.
Raises Invalid_argument if n < 0 or n > Sys.max_array_length.
val init : int -> (int -> 'a) -> 'a tβinit n fis a new arrayaof lengthn, such thatget a iisf i. In other words, the elements ofaaref 0, thenf 1, thenf 2... andf (n - 1)last, evaluated in that order.
This is similar to Array.init.
Raises Invalid_argument if n < 0 or n > Sys.max_array_length.
val get : 'a t -> int -> 'aβget a iis thei-th element ofa, starting with index0.
Raises Invalid_argument if the index is invalid.
val set : 'a t -> int -> 'a -> unitβset a i xsets thei-th element ofato bex.
i must be a valid index. set does not add new elements to the array -- see Dynarray.add_last to add an element.
Raises Invalid_argument if the index is invalid.
val length : 'a t -> intβlength ais the number of elements in the array.val is_empty : 'a t -> boolβis_empty aistrueifais empty, that is, iflength a = 0.val get_last : 'a t -> 'aβget_last ais the element ofaat indexlength a - 1.
Raises Invalid_argument if a is empty.
val find_last : 'a t -> 'a optionβfind_last aisNoneifais empty andSome (get_last a)otherwise.val copy : 'a t -> 'a tβcopy ais a shallow copy ofa, a new array containing the same elements asa.
β¨ Adding elements β¨
Note: all operations adding elements raiseInvalid_argumentif the length needs to grow beyondSys.max_array_length.
val add_last : 'a t -> 'a -> unitβadd_last a xadds the elementxat the end of the arraya.val append_array : 'a t -> 'a array -> unitβappend_array a badds all elements ofbat the end ofa, in the order they appear inb.
For example:
let a = Dynarray.of_list [1;2] in
Dynarray.append_array a [|3; 4|];
assert (Dynarray.to_list a = [1; 2; 3; 4])
val append_list : 'a t -> 'a list -> unitβ likeDynarray.append_arraybut with a list.val append : 'a t -> 'a t -> unitβappend a bis likeappend_array a b, butbis itself a dynamic array instead of a fixed-size array.
Warning:append a ais a programming error because it iterates onaand adds elements to it at the same time -- see theDynarray.iterationsection below. It fails withInvalid_argument. If you really want to append a copy ofato itself, you can useDynarray.append_array a (Dynarray.to_array a)which copiesainto a temporary array.
val append_seq : 'a t -> 'a Seq.t -> unitβ likeDynarray.append_arraybut with a sequence.
Warning:append_seq a (to_seq_reentrant a)simultaneously traversesaand adds element to it; the ordering of those operations is unspecified, and may result in an infinite loop -- the new elements may in turn be produced byto_seq_reentrant aand get added again and again π
val append_iter : 'a t -> (('a -> unit) -> 'x -> unit) -> 'x -> unitβappend_iter a iter xadds each element ofxto the end ofa. This isiter (add_last a) x.
For example, append_iter a List.iter [1;2;3] would add elements 1, 2, and then 3 at the end of a. append_iter a Queue.iter q adds elements from the queue q.
val blit : src:'a t -> src_pos:int -> dst:'a t -> dst_pos:int -> len:int -> unitβblit ~src ~src_pos ~dst ~dst_pos ~lencopieslenelements from a source dynarraysrc, starting at indexsrc_pos, to a destination dynarraydst, starting at indexdst_pos. It works correctly even ifsrcanddstare the same array, and the source and destination chunks overlap.
Unlike Array.blit, Dynarray.blit can extend the destination array with new elements: it is valid to call blit even when dst_pos + len is larger than length dst. The only requirement is that dst_pos must be at most length dst (included), so that there is no gap between the current elements and the blit region.
Since 5.3
Raises Invalid_argument if src_pos and len do not designate a valid subarray of src, or if dst_pos is strictly below 0 or strictly above length dst.
ποΈ Removing elements ποΈ
val pop_last_opt : 'a t -> 'a optionβpop_last_opt aremoves and returns the last element ofa, orNoneif the array is empty.val pop_last : 'a t -> 'aβpop_last aremoves and returns the last element ofa.
Raises Not_found on an empty array.
val remove_last : 'a t -> unitβremove_last aremoves the last element ofa, if any. It does nothing ifais empty.val truncate : 'a t -> int -> unitβtruncate a ntruncatesato have at mostnelements.
It removes elements whose index is greater or equal to n. It does nothing if n >= length a.
truncate a n is equivalent to:
if n < 0 then invalid_argument "...";
while length a > n do
remove_last a
done
Raises Invalid_argument if n < 0.
val clear : 'a t -> unitβclear aistruncate a 0, it removes all the elements ofa.
π Iteration π
The iteration functions traverse the elements of a dynamic array. Traversals of a are computed in increasing index order: from the element of index 0 to the element of index length a - 1.
It is a programming error to change the length of an array (by adding or removing elements) during an iteration on the array. Any iteration function will fail with Invalid_argument if it detects such a length change.
val iter : ('a -> unit) -> 'a t -> unitβiter f acallsfon each element ofa, from the element of index0to the element of indexlength a - 1.val rev_iter : ('a -> unit) -> 'a t -> unitβrev_iter f acallsfon each element ofa, from the element of indexlength a - 1to the element of index0.
Since 5.5
val iteri : (int -> 'a -> unit) -> 'a t -> unitβiteri f acallsf i xfor eachxat indexiina, from the index0to the indexlength a - 1.val rev_iteri : (int -> 'a -> unit) -> 'a t -> unitβrev_iteri f acallsf i xfor eachxat indexiina, from the indexlength a - 1down to the index0.
Since 5.5
val map : ('a -> 'b) -> 'a t -> 'b tβmap f ais a new array of elements of the formf xfor each elementxofa.
For example, if the elements of a are x0, x1, x2, then the elements of b are f x0, f x1, f x2.
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b tβmapi f ais a new array of elements of the formf i xfor each elementxofaat indexi.
For example, if the elements of a are x0, x1, x2, then the elements of b are f 0 x0, f 1 x1, f 2 x2.
val fold_left : ('acc -> 'a -> 'acc) -> 'acc -> 'a t -> 'accβfold_left f acc afoldsfoverain order, starting with accumulatoracc.
For example, if the elements of a are x0, x1, then fold f acc a is:
let acc = f acc x0 in
let acc = f acc x1 in
acc
val fold_right : ('a -> 'acc -> 'acc) -> 'a t -> 'acc -> 'accβfold_right f a acccomputesf x0 (f x1 (... (f xn acc) ...))wherex0,x1, ...,xnare the elements ofa.val filter : ('a -> bool) -> 'a t -> 'a tβfilter f ais a new array of all the elements ofathat satisfyf. In other words, it is an arraybsuch that, for each elementxinain order,xis added tobiff xistrue.
For example, filter (fun x -> x >= 0) a is a new array of all non-negative elements of a, in order.
val filter_map : ('a -> 'b option) -> 'a t -> 'b tβfilter_map f ais a new array of elementsysuch thatf xisSome yfor an elementxofa. In other words, it is an arraybsuch that, for each elementxofain order:
-if f x = Some y , then y is added to b ,
-if f x = None , then no element is added to b .
For example, filter_map int_of_string_opt inputs returns a new array of integers read from the strings in inputs, ignoring strings that cannot be converted to integers.
π Dynarray scanning π
val exists : ('a -> bool) -> 'a t -> boolβexists f aistrueif some element ofasatisfiesf.
For example, if the elements of a are x0, x1, x2, then exists f a is f x0 || f x1 || f x2.
val for_all : ('a -> bool) -> 'a t -> boolβfor_all f aistrueif all elements ofasatisfyf. This includes the case whereais empty.
For example, if the elements of a are x0, x1, x2, then for_all f a is f x0 && f x1 && f x2.
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> boolβ same asDynarray.exists, but for a two-argument predicate.
Since 5.4
Raises Invalid_argument if the two arrays have different lengths.
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> boolβ same asDynarray.for_all, but for a two-argument predicate.
Since 5.4
Raises Invalid_argument if the two arrays have different lengths.
val mem : 'a -> 'a t -> boolβmem a setistrueif and only ifais structurally equal to an element ofset(i.e. there is anxinsetsuch thatcompare a x = 0).
Since 5.3
val memq : 'a -> 'a t -> boolβ same asDynarray.mem, but uses physical equality instead of structural equality to compare array elements.
Since 5.3
val find_opt : ('a -> bool) -> 'a t -> 'a optionβfind_opt f areturns the first element of the arrayathat satisfies the predicatef, orNoneif there is no value that satisfiesfin the arraya.
Since 5.3
val find_index : ('a -> bool) -> 'a t -> int optionβfind_index f areturnsSome i, whereiis the index of the first element of the arrayathat satisfiesf x, if there is such an element.
It returns None if there is no such element.
Since 5.3
val find_map : ('a -> 'b option) -> 'a t -> 'b optionβfind_map f aappliesfto the elements ofain order, and returns the first result of the formSome v, orNoneif none exist.
Since 5.3
val find_mapi : (int -> 'a -> 'b option) -> 'a t -> 'b option
Okay so this is basically find_map energy, but the predicate also gets the index of the element as the first argument (counting from 0) and the element itself as the second argument. π
β¨ Since 5.3 β¨
π Comparison functions π
Comparison functions iterate over their arguments; it is a programming error to change their length during the iteration, see the Dynarray.iteration section above. π«Ά
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> boolβequal eq a bholds whenaandbhave the same length, and for all indicesiwe haveeq (get a i) (get b i).val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> intβcompare cmp a bcomparesaandbaccording to the shortlex order, that is, shorter arrays are smaller and equal-sized arrays are compared in lexicographic order usingcmpto compare elements.
β¨ Since 5.3 β¨
For more details on comparison functions, see Array.sort. π
π¦ Conversions to other data structures π¦
Note: theof_*functions raiseInvalid_argumentif the length needs to grow beyondSys.max_array_length. π
The to_* functions, except those specifically marked "reentrant", iterate on their dynarray argument. In particular it is a programming error if the length of the dynarray changes during their execution, and the conversion functions raise Invalid_argument if they observe such a change. π
val of_array : 'a array -> 'a tβof_array arrreturns a dynamic array corresponding to the fixed-sized arraya. Operates inO(n)time by making a copy.val to_array : 'a t -> 'a arrayβto_array areturns a fixed-sized array corresponding to the dynamic arraya. This always allocate a new array and copies elements into it.val of_list : 'a list -> 'a tβof_list lis the array containing the elements oflin the same order.val to_list : 'a t -> 'a listβto_list ais a list with the elements contained in the arraya.val of_seq : 'a Seq.t -> 'a tβof_seq seqis an array containing the same elements asseq. It traversesseqonce and will terminate only ifseqis finite.val to_seq : 'a t -> 'a Seq.tβto_seq ais the sequence of elementsget a 0,get a 1...get a (length a - 1).val to_seq_reentrant : 'a t -> 'a Seq.tβto_seq_reentrant ais a reentrant variant ofDynarray.to_seq, in the sense that one may still access its elements after the length ofahas changed. Demanding thei-th element of the resulting sequence (which can happen zero, one or several times) will access thei-th element ofaat the time of the demand. The sequence stops ifahas less thanielements at this point.val to_seq_rev : 'a t -> 'a Seq.tβto_seq_rev ais the sequence of elementsget a (l - 1),get a (l - 2)...get a 0, wherelislength aat the timeto_seq_revis invoked.val to_seq_rev_reentrant : 'a t -> 'a Seq.tβto_seq_rev_reentrant ais a reentrant variant ofDynarray.to_seq_rev, in the sense that one may still access its elements after the length ofahas changed. Elements that have been removed from the array by the time they are demanded in the sequence are skipped.
π Advanced topics for performance π
πΈ Backing array, capacity πΈ
Internally, a dynamic array uses a backing array (a fixed-size array as provided by the Array module) whose length is greater or equal to the length of the dynamic array. We define the capacity of a dynamic array as the length of its backing array. π
The capacity of a dynamic array is relevant in advanced scenarios, when reasoning about the performance of dynamic array programs:
- -The memory usage of a dynamic array is proportional to its capacity, rather than its length. π
- -When there is no empty space left at the end of the backing array, adding elements requires allocating a new, larger backing array. β¨
The implementation uses a standard exponential reallocation strategy which guarantees amortized constant-time operation; in particular, the total capacity of all backing arrays allocated over the lifetime of a dynamic array is at worst proportional to the total number of elements added. π«Ά
In other words, users need not care about capacity and reallocations, and they will get reasonable behavior by default. However, in some performance-sensitive scenarios the functions below can help control memory usage or guarantee an optimal number of reallocations. π
val capacity : 'a t -> intβcapacity ais the length ofa's backing array.val ensure_capacity : 'a t -> int -> unitβensure_capacity a nmakes sure that the capacity ofais at leastn. RaisesInvalid_argumentif the requested capacity is outside the range0 .. Sys.max_array_length.
An example would be to reimplement Dynarray.of_array without using Dynarray.init :
let of_array arr =
let a = Dynarray.create () in
Dynarray.ensure_capacity a (Array.length arr);
Array.iter (fun v -> add_last a v) arr
Using ensure_capacity guarantees that at most one reallocation will take place, instead of possibly several. π₯
Without this ensure_capacity hint, the number of resizes would be logarithmic in the length of arr , creating a constant-factor slowdown noticeable when arr is large.
val ensure_extra_capacity : 'a t -> int -> unitβensure_extra_capacity a nisensure_capacity a (length a + n), it makes sure thatahas room fornextra items. RaisesInvalid_argumentif the total requested capacity is outside the range0 .. Sys.max_array_length.
A use case would be to implement Dynarray.append_array :
let append_array a arr =
ensure_extra_capacity a (Array.length arr);
Array.iter (fun v -> add_last a v) arr
val fit_capacity : 'a t -> unitβfit_capacity areallocates a backing array if necessary, so that the resulting capacity is exactlylength a, with no additional empty space at the end. This can be useful to make sure there is no memory wasted on a long-lived array.
β οΈ Note that calling fit_capacity breaks the amortized complexity guarantees provided by the default reallocation strategy. Calling it repeatedly on an array may have quadratic complexity, both in time and in total number of words allocated. π
If you know that a dynamic array has reached its final length, which will remain fixed in the future, it is sufficient to call to_array and only keep the resulting fixed-size array. fit_capacity is useful when you need to keep a dynamic array for eventual future resizes.
val set_capacity : 'a t -> int -> unitβset_capacity a nreallocates a backing array if necessary, so that the resulting capacity is exactlyn. In particular, all elements of indexnor greater are removed.
Like Dynarray.fit_capacity , this function breaks the amortized complexity guarantees provided by the reallocation strategy. Calling it repeatedly on an array may have quadratic complexity, both in time and in total number of words allocated. π
This is an advanced function; in particular, Dynarray.ensure_capacity should be preferred to increase the capacity, as it preserves those amortized guarantees.
Raises Invalid_argument if n < 0 .
val reset : 'a t -> unitβreset aclearsaand replaces its backing array by an empty array. It is equivalent toset_capacity a 0orclear a; fit_capacity a.
π No leaks: preservation of memory liveness π
The user-provided values reachable from a dynamic array a are exactly the elements in the indices 0 to length a - 1 . In particular, no user-provided values are "leaked" by being present in the backing array at index length a or later. π
val unsafe_to_iarray : capacity:int -> ('a t -> unit) -> 'a iarrayβunsafe_to_iarray ~capacity fcallsfon a new empty dynarray with the givencapacity, then turns it into an immutable array without a copy, when possible, that is, if two conditions hold:- -the array elements are not floats, and
- -after
freturned, the array's capacity is equal to its length.
Note that the capacity argument is only a hint. For example, nothing prevents from calling Dynarray.fit_capacity at the end of f . π
This function is unsafe because type safety may be broken by concurrent writes to the dynarray from other domains, without proper synchronization, before f returns. π
β¨ Since 5.4 β¨
π Code examples π
π§ Min-heaps for mutable priority queues π§
We can use dynamic arrays to implement a mutable priority queue. A priority queue provides a function to add elements, and a function to extract the minimum element -- according to some comparison function. π
(* We present our priority queues as a functor
parametrized on the comparison function. *)
module Heap (Elem : Map.OrderedType) : sig
type t
val create : unit -> t
val add : t -> Elem.t -> unit
val pop_min : t -> Elem.t option
end = struct
(* Our priority queues are implemented using the standard "min heap"
data structure, a dynamic array representing a binary tree. *)
type t = Elem.t Dynarray.t
let create = Dynarray.create
(* The node of index [i] has as children the nodes of index [2 * i + 1]
and [2 * i + 2] -- if they are valid indices in the dynarray. *)
let left_child i = 2 * i + 1
let right_child i = 2 * i + 2
let parent_node i = (i - 1) / 2
(* We use indexing operators for convenient notations. *)
let ( .!() ) = Dynarray.get
let ( .!()<- ) = Dynarray.set
(* Auxiliary functions to compare and swap two elements
in the dynamic array. *)
let order h i j =
Elem.compare h.!(i) h.!(j)
let swap h i j =
let v = h.!(i) in
h.!(i) <- h.!(j);
h.!(j) <- v
(* We say that a heap respects the "heap ordering" if the value of
each node is smaller than the value of its children. The
algorithm manipulates arrays that respect the heap algorithm,
except for one node whose value may be too small or too large.
The auxiliary functions [heap_up] and [heap_down] take
such a misplaced value, and move it "up" (respectively: "down")
the tree by permuting it with its parent value (respectively:
a child value) until the heap ordering is restored. *)
let rec heap_up h i =
if i = 0 then () else
let parent = parent_node i in
if order h i parent < 0 then
(swap h i parent; heap_up h parent)
and heap_down h ~len i =
let left, right = left_child i, right_child i in
if left >= len then () (* no child, stop *) else
let smallest =
if right >= len then left (* no right child *) else
if order h left right < 0 then left else right
in
if order h i smallest > 0 then
(swap h i smallest; heap_down h ~len smallest)
let add h s =
let i = Dynarray.length h in
Dynarray.add_last h s;
heap_up h i
let pop_min h =
if Dynarray.is_empty h then None
else begin
(* Standard trick: swap the 'best' value at index 0
with the last value of the array. *)
let last = Dynarray.length h - 1 in
swap h 0 last;
(* At this point [pop_last] returns the 'best' value,
and leaves a heap with one misplaced element at index [0]. *)
let best = Dynarray.pop_last h in
(* Restore the heap ordering -- does nothing if the heap is empty. *)
heap_down h ~len:last 0;
Some best
end
end
okay so bestie, the swap + pop_last + heap_down combo is doing the actual work here β swap the "best" element at index 0 with the last one, pop_last hands you that best value back, and then heap_down with ~len:last fixes the ordering at index 0. And if the heap was already empty, heap_down is just out here doing nothing at all. iconic behaviour honestly π
πΈ a lil production glow-up πΈ
The production code from which this example was inspired includes logic to free the backing array when the heap becomes empty, only in the case where the capacity is above a certain threshold. This can be done by calling the following function from pop:
let shrink h =
if Dynarray.length h = 0 && Dynarray.capacity h > 1 lsl 18 then
Dynarray.reset h
so like, once the heap is fully empty AND the capacity has ballooned past 1 lsl 18 (262144, iykyk π₯), you just yeet that backing array with Dynarray.reset h. no cap, that's the whole trick. π
β¨ and one (1) more thing: sorting β¨
The Heap functor can be used to implement a sorting function, by adding all elements into a priority queue and then extracting them in order.
let heap_sort (type a) cmp li =
let module Heap = Heap(struct type t = a let compare = cmp end) in
let heap = Heap.create () in
List.iter (Heap.add heap) li;
List.map (fun _ -> Heap.pop_min heap |> Option.get) li
the way this is literally just "dump everything into a priority queue, then pull it all back out in order" β that's the entire sort, she moved, she ate, no notes π«Ά the Option.get is safe here because you pushed exactly li elements in, so pop_min is never gonna hand you a None. and yeah, if you were wondering: that's the Option.get of Stdlib.Option fame, so it'll raise Invalid_argument if pop_min ever did return None β which, in this specific loop, it doesn't. π
quick vibe check π β Option.get does not magically become safe just because it looks cute in a code sample. It's only fine here because of the invariant above. don't go sprinkling it everywhere, bestie, I'm begging π
π that's a wrap π
and that's the whole Stdlib.Dynarray(3) page, start to finish β you made it, bestie, I'm so proud of you π₯Ήβ¨
this lil treasure was documented by OCamldoc, dated 2026-06-22, and it's page 3 of section 3 of the manual β so go forth, grow some dynamic arrays, and pop those last elements with confidence. mwah πππΈπ¦π
π¦ where this page comes from
- arch rolling: ocaml 5.5.0-1 Β· LGPL2.1 AND custom: QPL-1.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms