2014-11-03 22 views
7

Ho un server socket che deve essere eseguito su un loop di accettare i clienti, così ho scoperto che nella programmazione funcional, un ciclo ricorsivo si usa:F # ciclo continuo in F #

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() 

E questo codice viene richiamato facendo:

Async.Start <| awaitConnections listener 

Considerando che l'applicazione viene eseguita continuamente, dovrei invece utilizzare un approccio iterativo? L'approccio rec crea stack di esecuzione nidificati?

Inoltre, mi piacerebbe fare qualcosa dopo la fine del ciclo, come:

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 

ma poi non riesco a compilare perché il tipo awaitConnections ritorno. Come potrei farlo? Lo sto facendo bene?

risposta

17

Sei certamente sulla buona strada! Ecco un semplice esempio che dimostra quello che devi fare:

// 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() 

Alcuni punti importanti:

  • Non si può davvero eseguire codice dopo un ciclo infinito finiture (è infinito!), Ma è può usare try .. finally per eseguire del codice quando il blocco viene annullato

  • noti che usare return! per looping ricorsiva è meglio - usando do! crea perdite di memoria.

  • È possibile annullare il calcolo utilizzando il token di cancellazione: è sufficiente passare il token all'avvio.

+3

Grazie! dove posso trovare maggiori informazioni su 'return!' vs. 'do!'? – vtortola

+1

Questa è una domanda caricata, ma farò un tentativo. Le espressioni di calcolo come 'async' sono zucchero sintattico per una serie di chiamate di funzione, e l'elemento di ritorno è il risultato delle funzioni. 'do!' definisce un'espressione che restituisce 'unit', che (in questo caso) crea fondamentalmente un nuovo oggetto' Async <'a>' che non fa altro che passare tra una chiamata ricorsiva e il successivo, 'return!' lo evita. Certo, questo prende in considerazione il modo in cui gli oggetti 'Async' lavorano dietro le quinte per far accadere tutto. –

+0

Grazie, molto utile. – vtortola