Foreach-Yield
Flix supports a special foreach-yield
construct which is used to build
collections from other collections. The foreach-yield
construct is related to
the foreach
construct, but should not be confused with it. The foreach
construct is for effectful iteration through collections whereas the
foreach-yield
construct is for building new collections.
We can use the foreach-yield
construct to build a new collection from existing
collections. For example:
def main(): Unit \ IO =
let fruits = List#{"Apple", "Pear", "Mango"};
let creams = List#{"Vanilla", "Stracciatella"};
let result: Set[(String, String)] =
foreach(fruit <- fruits; cream <- creams)
yield (fruit, cream);
println(result)
Here we construct a Set[(String, String)]
from two List[String]
. The type of
the resulting collection is typically inferred, but in this case we have placed
an explicit type annotation on the result
local variable to specify it.
We can use the foreach-yield
construct with several different types of
collections. For example:
def main(): Unit \ IO =
let c1 = Some(1);
let c2 = Set#{1, 2, 3};
let c3 = List#{1, 2, 3};
let result: Set[Int32] =
foreach(x <- c1; y <- c2; z <- c3)
yield x + y + z;
println(result)
Here we iterate through three collections c1
, c2
, and c3
and return a set
of the sums of their pairwise combinations.
The Collectable Trait
The workhorse behind the foreach-yield
construct is the Iterable
trait
(discussed in the previous section) and the Collectable
trait.
pub trait Collectable[t: Type] {
///
/// The element type of the Collectable.
///
type Elm[t]: Type
///
/// Run an Iterator collecting the results.
///
pub def collect(iter: Iterator[Collectable.Elm[t], ef, r]): t \ (ef + Collectable.Aef[t] + r)
}
Without going into details, the Collectable
trait is implemented by all
kinds of collections that can be constructed from an iterator. Notably, this
includes the List
, Chain
, and Set
data types.
Note: We cannot collect into a non-empty chain (
Nec
) or non-empty list (Nel
) since we cannot guarantee that anIterator
is non-empty.
Note: The
foreach-yield
construct can only collect into immutable collections. If we want to build a mutable collection, we should explicitly introduce its region, allocate the mutable collection, and add each element using a regularforeach
-loop.