What is the role of `while`-loops in computation expressions in F#?

computation-expression, f#, syntax

Solution

If you look at how computation expressions are evaluated, you'll see that

while foo() do
  printfn "step"
  yield bar()

is translated to something like

builder.While(fun () -> foo(), 
              builder.Delay(fun () -> 
                              printfn "step"
                              builder.Yield(bar()))))

This translation allows the body of the while loop to be evaluated multiple times. While your type signatures are accurate for some computation expressions (such as `seq` or `async`), note that the insertion of the call to `Delay` may result in a different signature. For instance, you could define a list builder like this:

type ListBuilder() =
  member x.Delay f = f
  member x.While(f, l) = if f() then l() @ (x.While(f, l)) else []
  member x.Yield(i) = [i]
  member x.Combine(l1,l2) = l1 @ l2()
  member x.Zero() = []
  member x.Run f = f()

let list = ListBuilder()

Now you can evaluate an expression like:

list {
  let x = ref 0
  while !x < 10 do
    yield !x
    x := !x + 1
}

to get the equivalent of `[0 .. 9]`.

Here, our `While` method has the signature `(unit -> bool) * (unit -> 'a list) -> 'a list`, rather than `(unit -> bool) * 'a list -> 'a list`. In general, when the `Delay` operation has type `(unit -> M<'a>) -> D<M<'a>>`, the `While` method's signature will be `(unit -> bool) * D<M<'a>> -> M<'a>`.

Problem

If you define a `While` method of the builder-object, you can use `while`-loops in your computation expressions. The signature of the `While` method is: ``` member b.While (predicate:unit->bool, body:M<'a>) : M<'a> ``` For comparison, the signature of the `For` method is: ``` member b.For (items:seq<'a>, body:unit->M<'a>) : M<'a> ``` You should notice that, in the `While`-method, the body is a simple type, and not a function as in the `For` method. You can embed some other statements, like `let` and function-calls inside your computation-expressions, but those can impossibly execute in a `while`-loop more than once. ``` builder { while foo() do printfn "step" yield bar() } ``` Why is the `while`-loop not executed more than once, but merely repeated? Why the significant difference from for-loops? Better yet, is there some intended strategy for using while-loops in computation-expressions?

Original source