IOList
IOList module: lazy, tree-structured string builder.
IOList represents a sequence of string segments as a tree, deferring concatenation until the list is flushed to IO. This avoids O(n²) copying when building large strings from many small pieces.
Usage pattern: let buf = IOList.append(IOList.from_string("Hello, "), IOList.from_string("world!")) println(IOList.to_string(buf)) -- "Hello, world!"
The key invariant: IOList is lazy. Use it as an accumulator and only call to_string or write to IO at the very end.
Types
Functions
Append two IOLists, returning a new IOList that represents their concatenation. Neither argument is modified.
IOList.to_string(IOList.append(IOList.from_string("foo"), IOList.from_string("bar")))
-- "foobar"
Internally, Empty nodes are dropped. No bytes are copied until `to_string`.
Cost: O(1) ā no flattening until `to_string`.Compute the total byte size of all segments in the IOList without flattening to a string.
IOList.byte_size(IOList.from_string("hello")) -- 5
IOList.byte_size(IOList.empty()) -- 0
Cost: O(number of nodes in the tree).The empty IOList ā contains no bytes.
IOList.to_string(IOList.empty()) -- ""
Cost: O(1).Wrap a String as an IOList leaf node.
IOList.to_string(IOList.from_string("hello")) -- "hello"
No copy is made; the IOList holds a reference to the original string.
Cost: O(1).Build an IOList from a List(String) by wrapping all elements as leaves.
IOList.to_string(IOList.from_strings(Cons("a", Cons("b", Cons("c", Nil)))))
-- "abc"
Cost: O(n) where n is the length of the list.Compute a FNV-1a 64-bit content hash over the IOList segments without first flattening to a single string. Returns a 16-character lowercase hex string suitable for use as an ETag value.
IOList.hash(IOList.from_string("hello")) -- some hex string
IOList.hash(IOList.empty()) -- "cbf29ce484222325"
The hash walks the IOList tree in order, hashing each `Str` segment's
bytes as encountered. The result is identical to hashing the fully
flattened string.
Suitable for ETag generation and fragment cache keying.
Cost: O(total bytes across all segments).Return true if the IOList contains no bytes.
IOList.is_empty(IOList.empty()) -- true
IOList.is_empty(IOList.from_string("")) -- true
IOList.is_empty(IOList.from_string("x")) -- false
Cost: O(1) for `Empty` and `Str`; O(n) for `Segments`.Prepend a String to an IOList.
IOList.to_string(IOList.prepend("Hello, ", IOList.from_string("world!")))
-- "Hello, world!"
Cost: O(1).Append a String to an IOList.
let buf = IOList.push(IOList.from_string("Hello"), ", world!")
IOList.to_string(buf) -- "Hello, world!"
Cost: O(1).Flatten an IOList into a single String by collecting all leaf segments and concatenating them.
IOList.to_string(IOList.empty()) -- ""
IOList.to_string(IOList.from_string("hello")) -- "hello"
This is the only function that allocates a full concatenated string.
Call it once at the end, not on every append.
Cost: O(total bytes across all segments).