gleam/list
Lists are an ordered sequence of elements and are one of the most common data types in Gleam.
New elements can be added and removed from the front of a list in constant time, while adding and removing from the end requires traversing and copying the whole list, so keep this in mind when designing your programs.
There is a dedicated syntax for prefixing to a list:
let new_list = [1, 2, ..existing_list]
And a matching syntax for getting the first elements of a list:
case list {
[first_element, ..rest] -> first_element
_ -> "this pattern matches when the list is empty"
}
Types
pub type ContinueOrStop(a) {
Continue(a)
Stop(a)
}
Constructors
-
Continue(a)
-
Stop(a)
Functions
pub fn all(
in list: List(a),
satisfying predicate: fn(a) -> Bool,
) -> Bool
Returns True
if the given function returns True
for all the elements in
the given list. If the function returns False
for any of the elements it
immediately returns False
without checking the rest of the list.
Examples
all([], fn(x) { x > 3 })
// -> True
all([4, 5], fn(x) { x > 3 })
// -> True
all([4, 3], fn(x) { x > 3 })
// -> False
pub fn any(
in list: List(a),
satisfying predicate: fn(a) -> Bool,
) -> Bool
Returns True
if the given function returns True
for any the elements in
the given list. If the function returns True
for any of the elements it
immediately returns True
without checking the rest of the list.
Examples
any([], fn(x) { x > 3 })
// -> False
any([4, 5], fn(x) { x > 3 })
// -> True
any([4, 3], fn(x) { x > 4 })
// -> False
any([3, 4], fn(x) { x > 3 })
// -> True
pub fn append(first: List(a), second: List(a)) -> List(a)
Joins one list onto the end of another.
This function runs in linear time, and it traverses and copies the first list.
Examples
append([1, 2], [3])
// -> [1, 2, 3]
pub fn chunk(in list: List(a), by f: fn(a) -> b) -> List(List(a))
Returns a list of chunks in which
the return value of calling f
on each element is the same.
Examples
[1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })
// -> [[1], [2, 2], [3], [4, 4, 6], [7, 7]]
pub fn combination_pairs(items: List(a)) -> List(#(a, a))
Return unique pair combinations of elements in the list
Examples
combination_pairs([1, 2, 3])
// -> [#(1, 2), #(1, 3), #(2, 3)]
pub fn combinations(items: List(a), by n: Int) -> List(List(a))
Return unique combinations of elements in the list.
Examples
combinations([1, 2, 3], 2)
// -> [[1, 2], [1, 3], [2, 3]]
combinations([1, 2, 3, 4], 3)
// -> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]
pub fn concat(lists: List(List(a))) -> List(a)
Deprecated: Use `list.flatten` instead.
Joins a list of lists into a single list.
This function traverses all elements twice.
Examples
concat([[1], [2, 3], []])
// -> [1, 2, 3]
pub fn contains(list: List(a), any elem: a) -> Bool
Determines whether or not a given element exists within a given list.
This function traverses the list to find the element, so it runs in linear time.
Examples
[] |> contains(any: 0)
// -> False
[0] |> contains(any: 0)
// -> True
[1] |> contains(any: 0)
// -> False
[1, 1] |> contains(any: 0)
// -> False
[1, 0] |> contains(any: 0)
// -> True
pub fn count(
list: List(a),
where predicate: fn(a) -> Bool,
) -> Int
Counts the number of elements in a given list satisfying a given predicate.
This function has to traverse the list to determine the number of elements, so it runs in linear time.
Examples
count([], fn(a) { a > 0 })
// -> 0
count([1], fn(a) { a > 0 })
// -> 1
count([1, 2, 3], int.is_odd)
// -> 2
pub fn drop(from list: List(a), up_to n: Int) -> List(a)
Returns a list that is the given list with up to the given number of elements removed from the front of the list.
If the element has less than the number of elements an empty list is returned.
This function runs in linear time but does not copy the list.
Examples
drop([1, 2, 3, 4], 2)
// -> [3, 4]
drop([1, 2, 3, 4], 9)
// -> []
pub fn drop_while(
in list: List(a),
satisfying predicate: fn(a) -> Bool,
) -> List(a)
Drops the first elements in a given list for which the predicate function returns True
.
Examples
drop_while([1, 2, 3, 4], fn (x) { x < 3 })
// -> [3, 4]
pub fn each(list: List(a), f: fn(a) -> b) -> Nil
Calls a function for each element in a list, discarding the return value.
Useful for calling a side effect for every item of a list.
import gleam/io
each(["1", "2", "3"], io.println)
// -> Nil
// 1
// 2
// 3
pub fn filter(
list: List(a),
keeping predicate: fn(a) -> Bool,
) -> List(a)
Returns a new list containing only the elements from the first list for
which the given functions returns True
.
Examples
filter([2, 4, 6, 1], fn(x) { x > 2 })
// -> [4, 6]
filter([2, 4, 6, 1], fn(x) { x > 6 })
// -> []
pub fn filter_map(
list: List(a),
with fun: fn(a) -> Result(b, c),
) -> List(b)
Returns a new list containing only the elements from the first list for
which the given functions returns Ok(_)
.
Examples
filter_map([2, 4, 6, 1], Error)
// -> []
filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) })
// -> [3, 5, 7, 2]
pub fn find(
in list: List(a),
one_that is_desired: fn(a) -> Bool,
) -> Result(a, Nil)
Finds the first element in a given list for which the given function returns
True
.
Returns Error(Nil)
if no such element is found.
Examples
find([1, 2, 3], fn(x) { x > 2 })
// -> Ok(3)
find([1, 2, 3], fn(x) { x > 4 })
// -> Error(Nil)
find([], fn(_) { True })
// -> Error(Nil)
pub fn find_map(
in list: List(a),
with fun: fn(a) -> Result(b, c),
) -> Result(b, Nil)
Finds the first element in a given list for which the given function returns
Ok(new_value)
, then returns the wrapped new_value
.
Returns Error(Nil)
if no such element is found.
Examples
find_map([[], [2], [3]], first)
// -> Ok(2)
find_map([[], []], first)
// -> Error(Nil)
find_map([], first)
// -> Error(Nil)
pub fn first(list: List(a)) -> Result(a, Nil)
Gets the first element from the start of the list, if there is one.
Examples
first([])
// -> Error(Nil)
first([0])
// -> Ok(0)
first([1, 2])
// -> Ok(1)
pub fn flat_map(
over list: List(a),
with fun: fn(a) -> List(b),
) -> List(b)
Maps the list with the given function into a list of lists, and then flattens it.
Examples
flat_map([2, 4, 6], fn(x) { [x, x + 1] })
// -> [2, 3, 4, 5, 6, 7]
pub fn flatten(lists: List(List(a))) -> List(a)
This is the same as concat
: it joins a list of lists into a single
list.
This function traverses all elements twice.
Examples
flatten([[1], [2, 3], []])
// -> [1, 2, 3]
pub fn fold(
over list: List(a),
from initial: b,
with fun: fn(b, a) -> b,
) -> b
Reduces a list of elements into a single value by calling a given function on each element, going from left to right.
fold([1, 2, 3], 0, add)
is the equivalent of
add(add(add(0, 1), 2), 3)
.
This function runs in linear time.
pub fn fold_right(
over list: List(a),
from initial: b,
with fun: fn(b, a) -> b,
) -> b
Reduces a list of elements into a single value by calling a given function on each element, going from right to left.
fold_right([1, 2, 3], 0, add)
is the equivalent of
add(add(add(0, 3), 2), 1)
.
This function runs in linear time.
Unlike fold
this function is not tail recursive. Where possible use
fold
instead as it will use less memory.
pub fn fold_until(
over list: List(a),
from initial: b,
with fun: fn(b, a) -> ContinueOrStop(b),
) -> b
A variant of fold that allows to stop folding earlier.
The folding function should return ContinueOrStop(accumulator)
.
If the returned value is Continue(accumulator)
fold_until will try the next value in the list.
If the returned value is Stop(accumulator)
fold_until will stop and return that accumulator.
Examples
[1, 2, 3, 4]
|> fold_until(0, fn(acc, i) {
case i < 3 {
True -> Continue(acc + i)
False -> Stop(acc)
}
})
// -> 3
pub fn group(
list: List(a),
by key: fn(a) -> b,
) -> Dict(b, List(a))
Groups the elements from the given list by the given key function.
Does not preserve the initial value order.
Examples
import gleam/dict
[Ok(3), Error("Wrong"), Ok(200), Ok(73)]
|> group(by: fn(i) {
case i {
Ok(_) -> "Successful"
Error(_) -> "Failed"
}
})
|> dict.to_list
// -> [
// #("Failed", [Error("Wrong")]),
// #("Successful", [Ok(73), Ok(200), Ok(3)])
// ]
import gleam/dict
group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 })
|> dict.to_list
// -> [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])]
pub fn index_fold(
over list: List(a),
from initial: b,
with fun: fn(b, a, Int) -> b,
) -> b
Like fold but the folding function also receives the index of the current element.
Examples
["a", "b", "c"]
|> index_fold([], fn(acc, item, index) { ... })
pub fn index_map(
list: List(a),
with fun: fn(a, Int) -> b,
) -> List(b)
Returns a new list containing only the elements of the first list after the function has been applied to each one and their index.
The index starts at 0, so the first element is 0, the second is 1, and so on.
Examples
index_map(["a", "b"], fn(x, i) { #(i, x) })
// -> [#(0, "a"), #(1, "b")]
pub fn interleave(list: List(List(a))) -> List(a)
Make a list alternating the elements from the given lists
Examples
interleave([[1, 2], [101, 102], [201, 202]])
// -> [1, 101, 201, 2, 102, 202]
pub fn intersperse(list: List(a), with elem: a) -> List(a)
Inserts a given value between each existing element in a given list.
This function runs in linear time and copies the list.
Examples
intersperse([1, 1, 1], 2)
// -> [1, 2, 1, 2, 1]
intersperse([], 2)
// -> []
pub fn is_empty(list: List(a)) -> Bool
Determines whether or not the list is empty.
This function runs in constant time.
Examples
is_empty([])
// -> True
is_empty([1])
// -> False
is_empty([1, 1])
// -> False
pub fn key_filter(
in keyword_list: List(#(a, b)),
find desired_key: a,
) -> List(b)
Given a list of 2-element tuples, finds all tuples that have a given key as the first element and returns the second element.
This function may be useful for interacting with Erlang code where lists of tuples are common.
Examples
key_filter([#("a", 0), #("b", 1), #("a", 2)], "a")
// -> [0, 2]
key_filter([#("a", 0), #("b", 1)], "c")
// -> []
pub fn key_find(
in keyword_list: List(#(a, b)),
find desired_key: a,
) -> Result(b, Nil)
Given a list of 2-element tuples, finds the first tuple that has a given key as the first element and returns the second element.
If no tuple is found with the given key then Error(Nil)
is returned.
This function may be useful for interacting with Erlang code where lists of tuples are common.
Examples
key_find([#("a", 0), #("b", 1)], "a")
// -> Ok(0)
key_find([#("a", 0), #("b", 1)], "b")
// -> Ok(1)
key_find([#("a", 0), #("b", 1)], "c")
// -> Error(Nil)
pub fn key_pop(
list: List(#(a, b)),
key: a,
) -> Result(#(b, List(#(a, b))), Nil)
Given a list of 2-element tuples, finds the first tuple that has a given key as the first element. This function will return the second element of the found tuple and list with tuple removed.
If no tuple is found with the given key then Error(Nil)
is returned.
Examples
key_pop([#("a", 0), #("b", 1)], "a")
// -> Ok(#(0, [#("b", 1)]))
key_pop([#("a", 0), #("b", 1)], "b")
// -> Ok(#(1, [#("a", 0)]))
key_pop([#("a", 0), #("b", 1)], "c")
// -> Error(Nil)
pub fn key_set(
list: List(#(a, b)),
key: a,
value: b,
) -> List(#(a, b))
Given a list of 2-element tuples, inserts a key and value into the list.
If there was already a tuple with the key then it is replaced, otherwise it is added to the end of the list.
Examples
key_set([#(5, 0), #(4, 1)], 4, 100)
// -> [#(5, 0), #(4, 100)]
key_set([#(5, 0), #(4, 1)], 1, 100)
// -> [#(5, 0), #(4, 1), #(1, 100)]
pub fn last(list: List(a)) -> Result(a, Nil)
Returns the last element in the given list.
Returns Error(Nil)
if the list is empty.
This function runs in linear time.
Examples
last([])
// -> Error(Nil)
last([1, 2, 3, 4, 5])
// -> Ok(5)
pub fn length(of list: List(a)) -> Int
Counts the number of elements in a given list.
This function has to traverse the list to determine the number of elements, so it runs in linear time.
This function is natively implemented by the virtual machine and is highly optimised.
Examples
length([])
// -> 0
length([1])
// -> 1
length([1, 2])
// -> 2
pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b)
Returns a new list containing only the elements of the first list after the function has been applied to each one.
Examples
map([2, 4, 6], fn(x) { x * 2 })
// -> [4, 8, 12]
pub fn map2(
list1: List(a),
list2: List(b),
with fun: fn(a, b) -> c,
) -> List(c)
Combines two lists into a single list using the given function.
If a list is longer than the other the extra elements are dropped.
Examples
map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y })
// -> [5, 7, 9]
map2([1, 2], ["a", "b", "c"], fn(i, x) { #(i, x) })
// -> [#(1, "a"), #(2, "b")]
pub fn map_fold(
over list: List(a),
from initial: b,
with fun: fn(b, a) -> #(b, c),
) -> #(b, List(c))
Similar to map
but also lets you pass around an accumulated value.
Examples
map_fold(
over: [1, 2, 3],
from: 100,
with: fn(memo, i) { #(memo + i, i * 2) }
)
// -> #(106, [2, 4, 6])
pub fn partition(
list: List(a),
with categorise: fn(a) -> Bool,
) -> #(List(a), List(a))
Partitions a list into a tuple/pair of lists by a given categorisation function.
Examples
import gleam/int
[1, 2, 3, 4, 5] |> partition(int.is_odd)
// -> #([1, 3, 5], [2, 4])
pub fn permutations(list: List(a)) -> List(List(a))
Returns all the permutations of a list.
Examples
permutations([1, 2])
// -> [[1, 2], [2, 1]]
pub fn pop(
in list: List(a),
one_that is_desired: fn(a) -> Bool,
) -> Result(#(a, List(a)), Nil)
Removes the first element in a given list for which the predicate function returns True
.
Returns Error(Nil)
if no such element is found.
Examples
pop([1, 2, 3], fn(x) { x > 2 })
// -> Ok(#(3, [1, 2]))
pop([1, 2, 3], fn(x) { x > 4 })
// -> Error(Nil)
pop([], fn(_) { True })
// -> Error(Nil)
pub fn pop_map(
in haystack: List(a),
one_that is_desired: fn(a) -> Result(b, c),
) -> Result(#(b, List(a)), Nil)
Removes the first element in a given list for which the given function returns
Ok(new_value)
, then returns the wrapped new_value
as well as list with the value removed.
Returns Error(Nil)
if no such element is found.
Examples
pop_map([[], [2], [3]], first)
// -> Ok(#(2, [[], [3]]))
pop_map([[], []], first)
// -> Error(Nil)
pop_map([], first)
// -> Error(Nil)
pub fn prepend(to list: List(a), this item: a) -> List(a)
Prefixes an item to a list. This can also be done using the dedicated syntax instead
let existing_list = [2, 3, 4]
[1, ..existing_list]
// -> [1, 2, 3, 4]
prepend(to: existing_list, this: 1)
// -> [1, 2, 3, 4]
pub fn range(from start: Int, to stop: Int) -> List(Int)
Creates a list of ints ranging from a given start and finish.
Examples
range(0, 0)
// -> [0]
range(0, 5)
// -> [0, 1, 2, 3, 4, 5]
range(1, -5)
// -> [1, 0, -1, -2, -3, -4, -5]
pub fn reduce(
over list: List(a),
with fun: fn(a, a) -> a,
) -> Result(a, Nil)
This function acts similar to fold, but does not take an initial state.
Instead, it starts from the first element in the list
and combines it with each subsequent element in turn using the given
function. The function is called as fun(accumulator, current_element)
.
Returns Ok
to indicate a successful run, and Error
if called on an
empty list.
Examples
[] |> reduce(fn(acc, x) { acc + x })
// -> Error(Nil)
[1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x })
// -> Ok(15)
pub fn repeat(item a: a, times times: Int) -> List(a)
Builds a list of a given value a given number of times.
Examples
repeat("a", times: 0)
// -> []
repeat("a", times: 5)
// -> ["a", "a", "a", "a", "a"]
pub fn rest(list: List(a)) -> Result(List(a), Nil)
Returns the list minus the first element. If the list is empty, Error(Nil)
is
returned.
This function runs in constant time and does not make a copy of the list.
Examples
rest([])
// -> Error(Nil)
rest([0])
// -> Ok([])
rest([1, 2])
// -> Ok([2])
pub fn reverse(list: List(a)) -> List(a)
Creates a new list from a given list containing the same elements but in the opposite order.
This function has to traverse the list to create the new reversed list, so it runs in linear time.
This function is natively implemented by the virtual machine and is highly optimised.
Examples
reverse([])
// -> []
reverse([1])
// -> [1]
reverse([1, 2])
// -> [2, 1]
pub fn scan(
over list: List(a),
from initial: b,
with fun: fn(b, a) -> b,
) -> List(b)
Similar to fold
, but yields the state of the accumulator at each stage.
Examples
scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i })
// -> [101, 103, 106]
pub fn shuffle(list: List(a)) -> List(a)
Takes a list, randomly sorts all items and returns the shuffled list.
This function uses float.random
to decide the order of the elements.
Example
range(1, 10) |> shuffle()
// -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5]
pub fn sized_chunk(
in list: List(a),
into count: Int,
) -> List(List(a))
Returns a list of chunks containing count
elements each.
If the last chunk does not have count
elements, it is instead
a partial chunk, with less than count
elements.
For any count
less than 1 this function behaves as if it was set to 1.
Examples
[1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2)
// -> [[1, 2], [3, 4], [5, 6]]
[1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3)
// -> [[1, 2, 3], [4, 5, 6], [7, 8]]
pub fn sort(
list: List(a),
by compare: fn(a, a) -> Order,
) -> List(a)
Sorts from smallest to largest based upon the ordering specified by a given function.
Examples
import gleam/int
sort([4, 3, 6, 5, 4, 1, 2], by: int.compare)
// -> [1, 2, 3, 4, 4, 5, 6]
pub fn split(
list list: List(a),
at index: Int,
) -> #(List(a), List(a))
Splits a list in two before the given index.
If the list is not long enough to have the given index the before list will be the input list, and the after list will be empty.
Examples
split([6, 7, 8, 9], 0)
// -> #([], [6, 7, 8, 9])
split([6, 7, 8, 9], 2)
// -> #([6, 7], [8, 9])
split([6, 7, 8, 9], 4)
// -> #([6, 7, 8, 9], [])
pub fn split_while(
list list: List(a),
satisfying predicate: fn(a) -> Bool,
) -> #(List(a), List(a))
Splits a list in two before the first element that a given function returns
False
for.
If the function returns True
for all elements the first list will be the
input list, and the second list will be empty.
Examples
split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 })
// -> #([1, 2, 3], [4, 5])
split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })
// -> #([1, 2, 3, 4, 5], [])
pub fn strict_zip(
list: List(a),
with other: List(b),
) -> Result(List(#(a, b)), Nil)
Takes two lists and returns a single list of 2-element tuples.
If one of the lists is longer than the other, an Error
is returned.
Examples
strict_zip([], [])
// -> Ok([])
strict_zip([1, 2], [3])
// -> Error(Nil)
strict_zip([1], [3, 4])
// -> Error(Nil)
strict_zip([1, 2], [3, 4])
// -> Ok([#(1, 3), #(2, 4)])
pub fn take(from list: List(a), up_to n: Int) -> List(a)
Returns a list containing the first given number of elements from the given list.
If the element has less than the number of elements then the full list is returned.
This function runs in linear time.
Examples
take([1, 2, 3, 4], 2)
// -> [1, 2]
take([1, 2, 3, 4], 9)
// -> [1, 2, 3, 4]
pub fn take_while(
in list: List(a),
satisfying predicate: fn(a) -> Bool,
) -> List(a)
Takes the first elements in a given list for which the predicate function returns True
.
Examples
take_while([1, 2, 3, 2, 4], fn (x) { x < 3 })
// -> [1, 2]
pub fn transpose(list_of_list: List(List(a))) -> List(List(a))
Transpose rows and columns of the list of lists.
Notice: This function is not tail recursive, and thus may exceed stack size if called, with large lists (on target JavaScript).
Examples
transpose([[1, 2, 3], [101, 102, 103]])
// -> [[1, 101], [2, 102], [3, 103]]
pub fn try_each(
over list: List(a),
with fun: fn(a) -> Result(b, c),
) -> Result(Nil, c)
Calls a Result
returning function for each element in a list, discarding
the return value. If the function returns Error
then the iteration is
stopped and the error is returned.
Useful for calling a side effect for every item of a list.
Examples
try_each(
over: [1, 2, 3],
with: function_that_might_fail,
)
// -> Ok(Nil)
pub fn try_fold(
over list: List(a),
from initial: b,
with fun: fn(b, a) -> Result(b, c),
) -> Result(b, c)
A variant of fold that might fail.
The folding function should return Result(accumulator, error)
.
If the returned value is Ok(accumulator)
try_fold will try the next value in the list.
If the returned value is Error(error)
try_fold will stop and return that error.
Examples
[1, 2, 3, 4]
|> try_fold(0, fn(acc, i) {
case i < 3 {
True -> Ok(acc + i)
False -> Error(Nil)
}
})
// -> Error(Nil)
pub fn try_map(
over list: List(a),
with fun: fn(a) -> Result(b, c),
) -> Result(List(b), c)
Takes a function that returns a Result
and applies it to each element in a
given list in turn.
If the function returns Ok(new_value)
for all elements in the list then a
list of the new values is returned.
If the function returns Error(reason)
for any of the elements then it is
returned immediately. None of the elements in the list are processed after
one returns an Error
.
Examples
try_map([1, 2, 3], fn(x) { Ok(x + 2) })
// -> Ok([3, 4, 5])
try_map([1, 2, 3], fn(_) { Error(0) })
// -> Error(0)
try_map([[1], [2, 3]], first)
// -> Ok([1, 2])
try_map([[1], [], [2]], first)
// -> Error(Nil)
pub fn unique(list: List(a)) -> List(a)
Removes any duplicate elements from a given list.
This function returns in loglinear time.
Examples
unique([1, 1, 1, 4, 7, 3, 3, 4])
// -> [1, 4, 7, 3]
pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b))
Takes a single list of 2-element tuples and returns two lists.
Examples
unzip([#(1, 2), #(3, 4)])
// -> #([1, 3], [2, 4])
unzip([])
// -> #([], [])
pub fn window(list: List(a), by n: Int) -> List(List(a))
Returns a list of sliding windows.
Examples
window([1,2,3,4,5], 3)
// -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
window([1, 2], 4)
// -> []
pub fn window_by_2(list: List(a)) -> List(#(a, a))
Returns a list of tuples containing two contiguous elements.
Examples
window_by_2([1,2,3,4])
// -> [#(1, 2), #(2, 3), #(3, 4)]
window_by_2([1])
// -> []
pub fn wrap(item: a) -> List(a)
Returns the given item wrapped in a list.
Examples
wrap(1)
// -> [1]
wrap(["a", "b", "c"])
// -> [["a", "b", "c"]]
wrap([[]])
// -> [[[]]]
pub fn zip(list: List(a), with other: List(b)) -> List(#(a, b))
Takes two lists and returns a single list of 2-element tuples.
If one of the lists is longer than the other, the remaining elements from the longer list are not used.
Examples
zip([], [])
// -> []
zip([1, 2], [3])
// -> [#(1, 3)]
zip([1], [3, 4])
// -> [#(1, 3)]
zip([1, 2], [3, 4])
// -> [#(1, 3), #(2, 4)]