Applicative For-Yield
In addition to the monadic forM expression, Flix supports an applicative
forA expression that builds on the Applicative trait. The forA
construct makes it simple to write error-handling code which uses the
Validation[e, t] data type.
Working with Validations
We can use the forA expression to validate user input while collecting all
errors.
enum Connection(String, String)
enum InvalidInput {
    case InvalidUserName,
    case InvalidPassword
}
def validateUser(s: String): Validation[InvalidInput, String] =
    if (8 <= String.length(s) and String.forAll(Char.isLetter, s))
        Validation.Success(s)
    else 
        Validation.Failure(Nec.singleton(InvalidInput.InvalidUserName))
def validatePass(s: String): Validation[InvalidInput, String] =
    if (12 <= String.length(s) and String.length(s) <= 20)
        Validation.Success(s)
    else 
        Validation.Failure(Nec.singleton(InvalidInput.InvalidPassword))
def connect(u: String, p: String): Validation[InvalidInput, Connection] = 
    forA (
        user <- validateUser(u);
        pass <- validatePass(p)
    ) yield Connection.Connection(user, pass)
The expression:
connect("Lucky Luke", "Ratata")
evaluates to:
Failure(Nec#{InvalidUserName, InvalidPassword})
which contains both input validation errors. On the other hand, the expression:
connect("luckyluke", "password12356789")
evaluates to:
Success(Connection(luckyluke, password12356789))
Applicatives are Independent Computations
We can write a monadic forM expression where the result of one monadic
operation is used as the input to another monadic operation. For example:
forM(x <- Some(123);  y <- Some(x)) 
    yield (x, y)
Here the value of y depends on x. That is, the computation of x and y
are not independent.
If we try to same with the applicative forA expression:
forA(x <- Some(123); y <- Some(x))
    yield (x, y)
then the Flix compiler emits a compiler error:
❌ -- Resolution Error --------------
>> Undefined name 'x'.
10 |         y <- Some(x)
                       ^
                       name not found
because the computations of x and y are independent and hence the value of
x is not in scope when we define the value of y.
Desugaring
The forA expression is syntactic sugar for uses of Functor.map and
Applicative.ap.
For example, the expression:
let o1 = Some(21);
let o2 = Some(42);
forA(x <- o1; y <- o2) 
    yield x + y;
is de-sugared to:
Applicative.ap(Functor.map(x -> y -> x + y, o1), o2)