List
Builtin.List(_item) :: # (opaque)
Returns the length of the list, which is equal to the number of items it contains.
One List can store up to I64.highest items on 64-bit targets and I32.highest on 32-bit targets like wasm.
This means the #U64 this function returns can always be safely converted to #I64 or #I32, depending on the target.
Returns the number of items the list can hold without triggering a memory allocation.
The capacity is always greater than or equal to the List.len, with one exception:
a list of zero-sized items, such as {}, never allocates at all, so its capacity is
always 0 no matter how many items it holds.
Check if the list is empty.
[1, 2, 3].is_empty()
[].is_empty()Iterate over the list from first to last.
Iterate over the list from last to first.
This walks the list backwards in place; it does not build a reversed
copy the way List.rev does.
expect Iter.fold([1, 2, 3].iter_rev(), [], |acc, item| acc.append(item)) == [3, 2, 1]
expect Iter.fold([].iter_rev(), [], |acc, item| acc.append(item)) == []Build a list from a pure Iter, pre-sizing the allocation from the
iterator's len_if_known when it is known up front. This is the from_iter
that Iter.collect dispatches to.
Put two lists together.
[1.I64, 2, 3].concat([4, 5])
[0.I64, 1, 2].concat([3, 4])Concatenate a list of lists into a single list, preserving order.
You may know a similar function named flatten in other languages, although
flatten sometimes flattens multiple levels of nesting; this only "flattens"
one level.
expect [[1.I64, 2], [3], [], [4, 5]].join() == [1, 2, 3, 4, 5]
expect List.join([["a", "b"], ["c"]]) == ["a", "b", "c"]Map each item to a list and concatenate the results, preserving order.
You may know a similar function named flat_map or concat_map in other languages.
expect [1.I64, 2, 3].join_map(|n| [n, n * 10]) == [1, 10, 2, 20, 3, 30]
expect ["a", "b"].join_map(|s| [s, s]) == ["a", "a", "b", "b"]Insert a separator between every pair of items in a list.
expect [1.I64, 2, 3].intersperse(0) == [1, 0, 2, 0, 3]
expect [1.I64].intersperse(0) == [1]Returns a list of the specified capacity without any items.
This is like calling List.reserve on an empty list. It's intended for
building up a list incrementally, for example by calling List.append on it:
expect {
var $squares = List.with_capacity(5)
for n in 1..=5 {
$squares = $squares.append(n * n)
}
$squares == [1, 4, 9, 16, 25]
}When the final length is known up front, List.with_capacity guarantees that
the appends which follow will not need to reallocate. Whether this is faster
than starting from [] is often marginal: List.append grows capacity
geometrically on its own, so the reallocations it performs are few and
amortized. The benefit is most pronounced when reallocation would otherwise
force a full copy of the list.
If you don't know the exact capacity, passing a value larger than necessary still avoids reallocation, at the cost of using more memory than is needed.
For more details, see List.reserve.
Increase a list's capacity by at least the given number of additional items.
When you already know how many items you are about to append, one
List.reserve up front replaces every reallocation those appends would
otherwise perform along the way:
expect {
ids = [1.U64, 2, 3]
# 1000 more items are coming, so make room for them all at once
var $all = ids.reserve(1000)
for id in 4..=1003 {
$all = $all.append(id)
}
$all.len() == 1003
}reserve(spare) aims for a capacity of exactly List.len(list) + spare
items; it trusts the request rather than rounding it up. If the list is not
shared and already has room for spare more items, it does nothing.
Otherwise it asks the allocator to grow the list to that size.
Note that the reserve above sits before the loop. Because List.reserve aims
for the exact size requested, it is a poor fit for use inside one: a reserve
that then gets filled completely leaves no room for the next one, so every
iteration goes back to the allocator, and the loop risks taking quadratic time
in the final length:
expect {
# The two appends fill the list back up to its exact capacity, so the
# next `reserve(2)` has to grow it again: one reallocation per iteration.
var $xs = []
while $xs.len() < 10 {
$xs = $xs.reserve(2)
$xs = $xs.append(0)
$xs = $xs.append(0)
}
$xs.len() == 10
}Reserve the whole amount once before the loop instead, or start from
List.with_capacity. A loop of plain List.append calls needs no help at
all: when an append has to grow the list, it grows the capacity
geometrically, which keeps such a loop linear in the number of items
appended.
Whether reserving is actually faster depends on the system allocator: many
allocators can extend an existing allocation in place, in which case the
reallocations that appends do on their own are cheap and List.reserve makes
little observable difference. The benefit is most pronounced when reallocation
would otherwise force a full copy of the list.
List.reserve is not free—when more capacity is needed, it calls into the
allocator, which may or may not have to move the existing items. Only use it
when you actually expect to make use of the extra capacity.
When you don't know exactly how many items you'll need, choosing a value somewhat higher than necessary is usually safe; a value that's too low may force later reallocation, while a value much higher than necessary just wastes memory.
If you plan to use List.reserve on an empty list, use List.with_capacity
instead.
Reduce memory usage by trimming unused capacity.
Sort a list using its items' default ordering.
Sort a list by a projected field. The projection is evaluated once per item, and items with equal fields retain their input order.
Sort a list using a custom three-way comparison function.
Sort a list in reverse using its items' default ordering.
Sort a list in reverse by a projected field. The projection is evaluated once per item, and items with equal fields retain their input order.
Sort a list in reverse using a custom three-way comparison function.
Returns True if the two lists have the same length and their items are pairwise equal.
Feed a list into a Hasher.
Add a single item to the end of a list.
[1.I64, 2, 3].append(4)
[0.I64, 1, 2].append(3)Add the Ok payload to the end of a list, or leave the list unchanged
if the value is Err.
expect List.append_if_ok([1, 2], Ok(3)) == [1, 2, 3]
expect List.append_if_ok([1, 2], Err(NotFound)) == [1, 2]Append count items to the end of the list, copying them from the
list itself beginning at index start.
The items are copied one at a time, and an item this call has just
appended can itself be copied. So if the copy reaches the original
end of the list, it keeps going into the freshly appended items,
which repeats the items from start onward for as long as
requested—copying 4 items from the last index of [1, 2]
appends 2, 2, 2, 2, and copying 3 items from index 0 of [1, 2]
appends 1, 2, 1.
Returns Err(OutOfBounds) if start is not an index in the list,
unless count is zero, which appends nothing.
expect [1, 2, 3].append_range_within(1, 2) == Ok([1, 2, 3, 2, 3])
expect [1, 2, 3].append_range_within(2, 4) == Ok([1, 2, 3, 3, 3, 3, 3])
expect [1, 2].append_range_within(0, 3) == Ok([1, 2, 1, 2, 1])
expect [1, 2].append_range_within(5, 1) == Err(OutOfBounds)Copy count items within this list, from src_index onward to
dest_index onward, overwriting what was there. The ranges may
overlap; the result is as if every source item were read before any
destination item were overwritten.
Returns Err(OutOfBounds) unless both whole ranges lie inside the
list, except that a zero count copies nothing.
expect [1, 2, 3, 4].copy_range_within(2, 0, 2) == Ok([1, 2, 1, 2])
expect [1, 2, 3, 4].copy_range_within(0, 1, 3) == Ok([2, 3, 4, 4])
expect [1, 2, 3].copy_range_within(2, 0, 2) == Err(OutOfBounds)Append a range of another list to this one, without building the
sublist as its own list first. Out-of-bounds ranges are clamped
exactly as List.sublist clamps them, producing a shorter or empty
append.
expect [1, 2].append_sublist([3, 4, 5, 6], { start: 1, len: 2 }) == [1, 2, 4, 5]
expect [1, 2].append_sublist([3, 4], { start: 1, len: 10 }) == [1, 2, 4]
expect [1, 2].append_sublist([3, 4], { start: 10, len: 2 }) == [1, 2]Add the Ok payload to the beginning of a list, or leave the list unchanged
if the value is Err.
expect List.prepend_if_ok([2, 3], Ok(1)) == [1, 2, 3]
expect List.prepend_if_ok([2, 3], Err(NotFound)) == [2, 3]Add a single item to the beginning of a list.
This is usually O(n), because every existing item has to move over by
one to make room at the front. To build up a list one item at a time,
append is much faster; if you need the items in the opposite order,
reverse the list once at the end.
The one exception is a list that is unique (nothing else refers to it)
and has had items removed from its front, for example by drop_first.
Removing items from the front leaves free space there, so prepending
onto such a list is O(1). This makes a pop-then-push pattern like
list.drop_first(1).prepend(item) fast.
expect [2, 3, 4].prepend(1) == [1, 2, 3, 4]
expect [].prepend(0) == [0]Returns the first item in the list, or ListWasEmpty if it was empty.
expect [1, 2, 3].first() == Ok(1)
expect [].first() == Err(ListWasEmpty)Returns an item from a list at the given index.
Returns Err OutOfBounds if the given index exceeds the List's length
expect [100, 200, 300].get(1) == Ok(200)
expect [100, 200, 300].get(5) == Err(OutOfBounds)Returns the list unchanged, hinting to the processor that the item at the given index is about to be read or written so it can start bringing that memory into its cache.
This is a no-op as far as the program's results go: it reads nothing, changes nothing, and an index past the end of the list is fine. Its only possible effect is on speed, and that effect can go either way. A hint for memory that was about to be loaded anyway, or that is never used, costs time; a hint issued too late does nothing. Only use it together with careful measurement, and keep it only where the measurement shows it helping.
It tends to pay off when the index is unpredictable, the list is much larger than the cache, and the index is known some steps before the item is used, as with a hash table's next bucket.
expect List.prefetched([10.U64, 20, 30], 1) == [10, 20, 30]Alias for List.get, enabling the future list[index] subscript operator.
Returns an item from a list at the given index.
Returns Err OutOfBounds if the given index exceeds the List's length
expect ["bird", "lizard"].subscript(0) == Ok("bird")
expect ["bird", "lizard"].subscript(5) == Err(OutOfBounds)Returns the item at the given index, wrapping back to the start when the index is past the end (the index is taken modulo the length).
You may know a similar function named getWrap, or indexing that wraps a
negative or out-of-range index, in other languages.
Returns Err(ListWasEmpty) if the list is empty.
expect ["a", "b", "c"].get_wrap(4) == Ok("b")Replaces the item at the given index with a new value.
expect [10, 20, 30].set(1, 99) == Ok([10, 99, 30])
expect [10, 20, 30].set(5, 99) == Err(OutOfBounds)Replaces the item at the given index, returning both the updated list and the value that was replaced.
expect [10, 20, 30].replace(1, 99) == Ok({ list: [10, 99, 30], prev: 20 })
expect [10, 20, 30].replace(5, 99) == Err(OutOfBounds)Updates the item at the given index by applying a function to it.
expect [10, 20, 30].update(1, |x| x + 5) == Ok([10, 25, 30])
expect [10, 20, 30].update(5, |x| x + 5) == Err(OutOfBounds)Exchanges the items at the two given indices.
expect [10, 20, 30, 40].swap(0, 3) == Ok([40, 20, 30, 10])
expect [10, 20, 30].swap(0, 5) == Err(OutOfBounds)Inserts an item at the given index, shifting later items toward the end. An index equal to the length appends the item; a greater index is out of bounds.
expect [1.I64, 2, 3].insert(1, 9) == Ok([1, 9, 2, 3])
expect [1.I64, 2, 3].insert(5, 9) == Err(OutOfBounds)Returns the reversed list.
expect [1, 2, 3].rev() == [3, 2, 1]
expect [].rev() == []Convert each item in the list to something new, by calling a conversion function on each of them. Then return a new list of the converted values.
expect [1, 2, 3].map(|num| num + 1) == [2, 3, 4]
expect ["", "a", "bc"].map(Str.is_empty) == [Bool.True, Bool.False, Bool.False]This works like List.map, except it also passes the index
of the item to the conversion function.
expect List.map_with_index([10, 20, 30], (|num, index| num + index)) == [10, 21, 32]Apply a binary function to pairs of items from two lists, returning a list of results. The result's length is the length of the shorter input list.
expect [1, 2, 3].map2([10, 20, 30], |a, b| a + b) == [11, 22, 33]
expect [1, 2, 3, 4, 5].map2([10, 20], |a, b| a + b) == [11, 22]Apply a ternary function to triples of items from three lists, returning a list of results. The result's length is the length of the shortest input list.
expect [1, 2, 3].map3([10, 20, 30], [100, 200, 300], |a, b, c| a + b + c) == [111, 222, 333]
expect [1, 2, 3, 4, 5].map3([10, 20], [100, 200, 300], |a, b, c| a + b + c) == [111, 222]Apply a quaternary function to groups of items from four lists, returning a list of results. The result's length is the length of the shortest input list.
expect [1, 2, 3].map4([10, 20, 30], [100, 200, 300], [1000, 2000, 3000], |a, b, c, d| a + b + c + d) == [1111, 2222, 3333]
expect [1, 2, 3, 4, 5].map4([10, 20], [100, 200, 300], [1000, 2000, 3000, 4000], |a, b, c, d| a + b + c + d) == [1111, 2222]Run the given function on each item of a list, and return all the
items for which the function returned Bool.True.
[1.I64, 2, 3, 4].keep_if(|num| num > 2)Performance Details
List.keep_if always returns a list that takes up exactly the same amount
of memory as the original, even if its length decreases. This is because it
can't know in advance exactly how much space it will need, and if it guesses a
length that's too low, it would have to re-allocate.
(If you want to do an operation like this which reduces the memory footprint
of the resulting list, you can do two passes over the list with List.fold - one
to calculate the precise new size, and another to populate the new list.)
If given a unique list, List.keep_if will mutate it in place to assemble the appropriate list.
If that happens, this function will not allocate any new memory on the heap.
If all items in the list end up being kept, Roc will return the original
list unaltered.
Run the given function on each item of a list, and return all the
items for which the function returned Bool.False.
[1.I64, 2, 3, 4].drop_if(|num| num > 2)Performance Details
List.drop_if has the same performance characteristics as List.keep_if.
See its documentation for details on those characteristics!
Run the given function on each item of a list, and return a list of
the values it wrapped in Ok. Items the function maps to Err are
dropped. Use List.keep_errs to keep the Err values instead.
expect [1.I64, 2, 3, 4].keep_oks(|n| if n.is_even() { Ok(n) } else { Err({}) }) == [2, 4]Run the given function on each item of a list, and return a list of
the values it wrapped in Err. Items the function maps to Ok are
dropped. Use List.keep_oks to keep the Ok values instead.
expect [1.I64, 2, 3, 4].keep_errs(|n| if n.is_even() { Ok(n) } else { Err(n) }) == [1, 3]Run the given function on each item of a list, and return the
number of items for which the function returned Bool.True.
expect [1, -2, -3].count_if(I64.is_negative) == 2
expect [1, 2, 3].count_if(|num| num > 1) == 2Like List.map, but the transform can fail. Runs a Try-returning
function on each item and collects the Ok values. Stops at the first
Err and returns it, so the result is either all the transformed values
or the first failure.
expect [1.I64, 2, 3].map_try(|n| if n < 3 { Ok(n) } else { Err(TooBig) }) == Err(TooBig)Like List.map_try, but the transform is effectful. It runs on each
item until one returns Err, then stops.
rows.map_try!(|row| SQL.insert!("INSERT INTO t VALUES (?)", [row]))Like List.keep_if, but the predicate can fail. Runs a Try-returning
predicate on each item, keeping the ones it maps to Ok(Bool.True).
Stops at the first Err and returns it.
expect [1.I64, 2, 3].keep_if_try(|n| Ok(n > 1)) == Ok([2, 3])Like List.keep_if_try, but the predicate is effectful. It runs on each
item until one returns Err, then stops.
users.keep_if_try!(|user| SQL.query_bool!("SELECT is_active FROM users WHERE id = ?", [user.id]))Like List.fold, but the step function can fail. Threads state through
the list, and if a step returns Err, stops and returns it.
expect [1.I64, 2, 3].fold_try(0, |sum, n| if n < 3 { Ok(sum + n) } else { Err(Stop) }) == Err(Stop)Like List.fold_try, but the step function is effectful. It runs on each
item until one returns Err, then stops.
events.fold_try!(State.init, |state, event| Store.apply!(state, event))Build a value using each item in the list.
Starting with a given state value, this folds through each item in the
list from first to last, running a given step function on that item
which updates the state. It returns the final state at the end.
[2, 4, 8].fold(0, U64.plus)This returns 14 because:
statestarts at 0- Each
steprunsstate.plus(item), and the return value becomes the newstate.
Here is a table of how state changes as List.fold folds over the items
[2, 4, 8] using U64.plus as its step function to determine the next state.
| state | item | U64.plus(state, item) |
|---|---|---|
| 0 | ||
| 0 | 2 | 2 |
| 2 | 4 | 6 |
| 6 | 8 | 14 |
The following returns -6:
[1, 2, 3].fold(0, I64.minus)Note that in other languages, fold is sometimes called reduce,
fold_left, or foldl.
Like List.fold, but at each step the function also receives the index of the current item.
Same as List.fold, except you can stop folding early.
Performance Details
Compared to List.fold, this can potentially visit fewer items (which can
improve performance) at the cost of making each step take longer.
However, the added cost to each step is extremely small, and can easily
be outweighed if it results in skipping even a small number of items.
As such, it is typically better for performance to use this over List.fold
if returning Break earlier than the last item is expected to be common.
fold_with_index_until : List(item), state, (state, item, U64 -> [Continue(state), Break(state)]) -> stateSame as List.fold_with_index, except you can stop folding early.
Like List.fold, but walks the list from last to first. The step function
receives the item first and the current state second.
expect [1, 2, 3].fold_rev(0, I64.minus) == 2Here is a table of how state changes as List.fold_rev folds over the
items [1, 2, 3] using I64.minus as its step function.
| state | item | I64.minus(item, state) |
|---|---|---|
| 0 | ||
| 0 | 3 | 3 |
| 3 | 2 | -1 |
| -1 | 1 | 2 |
Note that in other languages, fold_rev is sometimes called reduce_right,
fold_right, or foldr.
Returns Bool.True if the first list starts with the second list.
If the second list is empty, this always returns Bool.True; every list
is considered to "start with" an empty list.
If the first list is empty, this only returns Bool.True if the second list is empty.
Returns Bool.True if the first list ends with the second list.
If the second list is empty, this always returns Bool.True; every list
is considered to "end with" an empty list.
If the first list is empty, this only returns Bool.True if the second list is empty.
Run the given predicate on each item of the list, returning Bool.True if
any of the items satisfy it.
expect [1, 2, 3].any(|n| n % 2 == 0)
expect ![1, 2, 3].any(|n| n < 0)Returns Bool.True if the list contains an item equal to the given value.
expect [1, 2, 3].contains(2)
expect ![1, 2, 3].contains(4)Run the given predicate on each item of the list, returning Bool.True if
all of the items satisfy it.
expect [2, 4, 6].all(|n| n % 2 == 0)
expect ![1, 2, 3].all(|n| n % 2 == 0)Returns the last item in the list, or ListWasEmpty if it was empty.
expect [1, 2, 3].last() == Ok(3.0)
expect [].last() == Err(ListWasEmpty)Create a list with a single item in it.
expect List.single(42) == [42.0]Remove the item at the given index. If the index is out of bounds, the list is returned unchanged.
expect [10, 20, 30, 40].drop_at(1) == [10, 30, 40]
expect [10, 20, 30].drop_at(5) == [10, 20, 30]Removes the item at the given index by moving the last item into its
place, so the order of the remaining items is not preserved. This is O(1),
unlike drop_at, which shifts every later item down.
Returns the list unchanged if the index is out of bounds.
expect [1.I64, 2, 3, 4].drop_swap(1) == [1, 4, 3]Return a sublist of the list starting at start and containing up to len
items. Out-of-bounds ranges are clamped, producing a shorter or empty list.
expect [1, 2, 3, 4, 5].sublist({ start: 1, len: 3 }) == [2, 3, 4]
expect [1, 2, 3].sublist({ start: 1, len: 10 }) == [2, 3]
expect [1, 2, 3].sublist({ start: 10, len: 2 }) == []Split a list into chunks of at most chunk_size items, in order. The
final chunk is shorter when the length is not a multiple of chunk_size,
and a chunk_size of 0 produces an empty list.
expect [1.I64, 2, 3, 4, 5].chunks_of(2) == [[1, 2], [3, 4], [5]]
expect [1.I64, 2, 3].chunks_of(10) == [[1, 2, 3]]Return the first n items of the list. If the list has fewer than n
items, the entire list is returned.
expect [1, 2, 3, 4, 5].take_first(3) == [1, 2, 3]
expect [1, 2].take_first(10) == [1, 2]Removes every item while preserving the current capacity.
expect [1.I64, 2, 3].clear() == []Returns the given number of items from the end of the list.
expect [1, 2, 3, 4, 5, 6, 7, 8].take_last(4) == [5, 6, 7, 8]If there are fewer items in the list than the requested number, the entire list is returned.
expect [1, 2].take_last(5) == [1, 2]To remove items from the end of the list, use List.take_first.
To remove items from both the beginning and end of the list,
use List.sublist.
Drops n items from the beginning of the list. If n is larger than the
list length, an empty list is returned.
expect [1, 2, 3, 4, 5].drop_first(2) == [3, 4, 5]
expect [1, 2, 3].drop_first(10) == []Drops n items from the end of the list. If n is larger than the
list length, an empty list is returned.
expect [1, 2, 3, 4, 5].drop_last(2) == [1, 2, 3]
expect [1, 2, 3].drop_last(10) == []Find the first item in a list that satisfies a given predicate, returning it wrapped in Ok if found, or Err(NotFound) if no such item exists.
expect [1, 2, 3, 4].find_first(|x| x % 2 == 0) == Ok(2)Find the last item in a list that satisfies a given predicate, returning it wrapped in Ok if found, or Err(NotFound) if no such item exists.
expect [1, 2, 3, 4].find_last(|x| x % 2 == 0) == Ok(4)Find the index of the first item in a list that satisfies a given predicate, returning it wrapped in Ok if found, or Err(NotFound) if no such item exists.
expect [1, 2, 3, 4].find_first_index(|x| x > 1) == Ok(1)Find the index of the last item in a list that satisfies a given predicate, returning it wrapped in Ok if found, or Err(NotFound) if no such item exists.
expect [1, 2, 3, 4].find_last_index(|x| x < 4) == Ok(2)expect [0, 1, 2, 3, 4].split_at(2) == { before: [0, 1], others: [2, 3, 4] }Split a list into sublists using a specified delimiter item.
Consecutive delimiters and delimiters at the start or end of the list produce empty sublists at the corresponding positions.
expect [1, 2, 1, 2, 3].split_on(1) == [[], [2], [2, 3]]
expect [1, 1, 2].split_on(1) == [[], [], [2]]Split a list into sublists using a predicate function to identify delimiters.
Consecutive delimiters and delimiters at the start or end of the list produce empty sublists at the corresponding positions.
expect [0, 1, 2, 3, 4].split_if(|x| x % 2 == 0) == [[], [1], [3], []]Split a list into sublists using a specified delimiter list.
expect [1, 2, 3, 4, 5].split_on_list([2, 3]) == [[1], [4, 5]]split_first : List(a), a -> Try({ before : List(a), after : List(a) }, [NotFound]) where [a.is_eq : a, a -> Bool]Split a list into two parts at the first occurrence of a specified delimiter item, returning the part before the delimiter and the part after it. If the delimiter is not found, return Err(NotFound).
expect [0, 1, 2, 1, 2].split_first(2) == Ok({ before: [0, 1], after: [1, 2] })split_last : List(a), a -> Try({ before : List(a), after : List(a) }, [NotFound]) where [a.is_eq : a, a -> Bool]Split a list into two parts at the last occurrence of a specified delimiter item, returning the part before the delimiter and the part after it. If the delimiter is not found, return Err(NotFound).
expect [0, 1, 2, 1, 2].split_last(1) == Ok({ before: [0, 1, 2], after: [2] })Build a list by repeating the given value n times.
expect List.repeat(0, 3) == [0, 0, 0]
expect List.repeat("hi", 0) == []Sum the items of a list. Works for any type that implements plus and
default methods, such as the numeric types.
default is called only when the list is empty; otherwise it is never called
at all. Summing begins at the first item and uses plus to combine the
rest, so a single-item list returns that item without calling either
method, and default does not need to be an identity value for plus.
expect List.sum([1.I64, 2, 3, 4]) == 10
expect List.sum([42.I64]) == 42
expect List.sum([]) == 0.I64Find the minimum item in a list, or Err(ListWasEmpty) if the list is empty.
Works for any type that implements min.
Find the maximum item in a list, or Err(ListWasEmpty) if the list is empty.
Works for any type that implements max.
encoder_for : encoding -> (List(item), state -> Try(state, err))
where [
item.encoder_for : encoding -> (item, state -> Try(state, err)),
encoding.encode_list : state, U64, (container_state, (container_state, (state -> Try(state, err)) -> Try(container_state, err)) -> Try(container_state, err)) -> Try(state, err),
]Build an encoder for a list using a format that provides a list encoding method.
decode : src, fmt -> (Try(List(item), err), src)
where [
fmt.decode_list : fmt, src, (src, fmt -> (Try(item, err), src)) -> (Try(List(item), err), src),
item.decode : src, fmt -> (Try(item, err), src),
]Decode a list using a format that provides decode_list