Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# Continuous loop in F#

I have a socket server that needs to run on a loop accepting clients, so I found out that in funcional programming, a recursive loop is used:

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop ()=
        async { 
            let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token) )
            printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString())
            Async.Start <| awaitMessages ws
            do! loop()}
    loop()

And this code is invoked by doing:

Async.Start <| awaitConnections listener

Considering that the app runs continuously, should I use a iterative approach instead? Does the rec approach creates nested execution stacks?

Also, I would like to do something after the loop ends, like:

let awaitConnections (wsl:WebSocketListener) = 
    let rec loop ()=
        async { 
            let! ws = Async.AwaitTask (wsl.AcceptWebSocketAsync(cancellation.Token) )
            printfn "Connection from %s" (ws.RemoteEndpoint.Address.ToString())
            Async.Start <| awaitMessages ws
            do! loop()}
    loop()
    printf "The loop ended" // <-- this line

But then it cannot compile because the awaitConnections return type. How could I do that? Am I doing this right?

like image 219
vtortola Avatar asked Nov 03 '14 00:11

vtortola


1 Answers

You're certainly on the right track! Here is a simple example that demonstrates what you need to do:

// Loop that keeps running forever until an exception happens
let rec loop () = async {
  do! Async.Sleep(1000)
  printfn "Working"
  return! loop () }

// Call the loop in a try .. finally block to run some cleanup code at the end
let main () = async {
  try 
    do! loop () 
  finally 
    printfn "That's it!" }

// Start the work in the background and create a cancellation token
let cts = new System.Threading.CancellationTokenSource()
Async.Start(main (), cts.Token)
// To cancel it, just call: cts.Cancel()

A few important points:

  • You cannot really run code after an infinite loop finishes (it is infinite!) but you can use try .. finally to run some code when the block is cancelled

  • Note that using return! for recursive looping is better - using do! creates memory leaks.

  • You can cancel computation using cancellation token - just pass the token when starting it.

like image 95
Tomas Petricek Avatar answered Nov 16 '22 02:11

Tomas Petricek