Reputation: 9460
Whilst watching a Pluralsight course by Tomas Petricek (who I assume knows what he is talking about), I saw code like the following...
let echo =
MailboxProcessor<string>.Start(fun inbox ->
async {
while do true
let! msg = inbox.Receive()
printfn "Hello %s" msg
})
Ignore the fact that this was to demo agents, I'm interested in the inner function, which uses while do true
to keep it running indefinitely.
Whilst looking around for other example of agents, I saw that many other people use code like this...
let counter =
MailboxProcessor.Start(fun inbox ->
let rec loop n =
async { do printfn "n = %d, waiting..." n
let! msg = inbox.Receive()
return! loop(n+msg) }
loop 0)
Code copied from Wikibooks.
The inner function here is recursive, and is started off by calling it with a base value before the main function declaration ends.
Now I realise that in the second case recursion is a handy way of passing a private value to the inner function without having to use a mutable local value, but is there any other reason to use recursion here rather than while do true
? Would there be any benefit in writing the first code snippet using recursion?
I find the non-recursive version much easier to read (subjective opinion of course), which seems like a good reason to use that whenever possible.
Upvotes: 4
Views: 1088
Reputation: 11577
In F# for
and while
loop expressions lacks capabilities common in other languages:
continue
- Skip the rest of the loop and restart from the top of loop expression.break
- Stop the loop prematurely.If you want continue
and break
don't do what I did in the beginning a write a very complex test expression for while
loops. Instead tail recursion is the best answer in F#:
let vs : int [] = ...
let rec findPositiveNumberIndex i =
if i < vs.Length then
if vs.[i] > 0 then
Some i
else
findPositiveNumberIndex (i + 1)
else
None
match findPositiveNumberIndex 0 with
| Some i -> printfn "First positive number index: %d" i
| None -> printfn "No positive numbers found"
In code like this F# applies something call tail-call optimization (TCO) which transforms the code above into a while
loop with break
. This means that we won't run out of stack space and the loop is efficient. TCO is a feature that C# lacks so we don't want to write code like above in C#.
Like others are saying with tail recursion you can sometimes avoid mutable state but that's not all.
With tail recursion your loop expressions can return a result which is nice.
In addition, if you like to iterate fast in F# over types like int64
or with increment other than 1
and -1
you have to rely on tail recursion. The reason is that F# only does efficient for
expressions for ints and increments of 1
and -1
.
for i in 0..100 do
printfn "This is a fast loop"
for i in 0..2..100 do
printfn "This is a slow loop"
for i in 0L..100L do
printfn "This is a slow loop"
Sometimes when you are hunting for performance a trick is to loop towards 0
(saves a CPU register). Unfortunately the way the F# generates for
loop code this doesn't perform as well one would hope:
for i = 100 downto 0 do
printfn "Unfortunately this is not as efficient as it can be"
Tail recursion towards 0
saves the CPU register.
(Unfortunately the F# compiler don't merge the test and the loop instruction for tail-recursion so it's not so good as it could be)
Upvotes: 2
Reputation: 243061
Talking about MailboxProcessor
specifically, I think the choice depends on what exactly you are doing. In general, you can always use while
loop or recursion.
Recursion makes it easier to use immutable state and I find while
loop nicer if you have no state or if you use mutable state. Using mutable state is often quite useful, because MailboxProcessor
protects you from concurrent accesses and you can keep the state local, so things like Dictionary
(efficient hash table) are often useful.
In general:
while
Dictionary
or ResizeArray
), I'd go for while
Upvotes: 15
Reputation: 64
In many cases it depends on how you like to code. Like in your example. Everything you can write recursiv you can also write with a loop, but sometime like with recursive data structures it is easier to write the in a recursive style. At university I learned that with recursive programming you only have to look at your next step which is pretty handy!
You might be interested in this question as it explains my answer a bit further: recursion versus iteration
Upvotes: 3