For-Each and For-Yield
Note: This feature is experimental and not yet intended for use.
In Flix, as in other functional programming languages, most iteration is expressed either through recursion or with combinators (e.g. map
or foldLeft
).
That said, Flix has syntactic sugar for two common types of loops: for-each and for yield.
For Each
The for-each construct is useful for iterating over a collection
and apply some transformation to each element and works particularly
well with mutable collections.
This is due to the fact that the for-each loop is actually just
syntactic sugar for a call to Iterable.foreach
which has return
type Unit
.
Thus, for the loop to be useful the body of the loop should have an effect.
However, before going any further an example is in order.
To use the for-each loop an instance of Iterable
on the collection is required.
For this example we will use a MutList
.
def main(): Unit & Impure = region r {
use MutList.push!;
let l = new MutList(r)
!> push!(1)
!> push!(2)
!> push!(3);
foreach (x <- l)
println(x)
}
For Yield
Flix also supports a for-yield construct which is similar to Scala's for-comprehensions
or to Haskell's list comprehension. The for-yield construct is simply syntactic sugar
for uses of point
and flatMap
(which requires an instance of the Monad
type class).
The for-yield construct also supports a guard-expression which, when used,
additionally requires an instance of the MonadZero
type class.
The for-yield expression:
let l1 = 1 :: 2 :: Nil;
let l2 = 1 :: 2 :: Nil;
for (x <- l1; y <- l2)
yield (x, y)
evaluates to the list:
(1, 1) :: (1, 2) :: (2, 1) :: (2, 2) :: Nil
The for-yield expression:
let l1 = 1 :: 2 :: Nil;
let l2 = 1 :: 2 :: Nil;
for (x <- l1; y <- l2; if x < y)
yield (x, y)
evaluates to the list:
(1, 2) :: Nil
We can use for-yield on any data type which implements the Monad
type class. For example, we can iterate through non-empty lists:
let l1 = Nel(1, 2 :: Nil);
let l2 = Nel(1, 2 :: Nil);
for (x <- l1; y <- l2)
yield (x, y)
which evaluates to the non-empty list:
Nel((1, 1), (1, 2) :: (2, 1) :: (2, 2) :: Nil)
Note: We cannot use an
if
-guard with a non-empty list because such anif
-guard requires an instance of theMonadZero
type class which is not implemented by non-empty list. Intuitively, we cannot use a filter in combination with a data structures that cannot be empty.