15

What is the Scala equivalent of F#'s async workflows?

For example, how would following F# snippet translate to idiomatic Scala?

open System.Net
open Microsoft.FSharp.Control.WebExtensions

let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
                "MSDN", "http://msdn.microsoft.com/"
                "Bing", "http://www.bing.com"
              ]

let fetchAsync(name, url:string) =
    async { 
        try
            let uri = new System.Uri(url)
            let webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            printfn "Read %d characters for %s" html.Length name
        with
            | ex -> printfn "%s" (ex.Message);
    }

let runAll() =
    urlList
    |> Seq.map fetchAsync
    |> Async.Parallel 
    |> Async.RunSynchronously
    |> ignore

runAll()
pharoah 121
  • 151
  • 3
  • 3
    Don't tell anyone but F#'s workflows are just monads in disguise. If Scala has some syntax for monads (I don't speak Scala, so I can't tell), then that's the equivalent. – R. Martinho Fernandes Apr 07 '11 at 10:48
  • 2
    that's not totaly true - it's the continuation-monad plus a lot extra stuff for exception handling and usage of the ThreadPool/Tasks, etc. - it's non-trivial to just redo this – Random Dev Jun 25 '12 at 11:06
  • @R.MartinhoFernandes "Don't tell anyone but F#'s workflows are just monads in disguise". That is not quite correct. *Computation expressions* are the general framework for monadic syntax in F# of which asynchronous workflows are a specific form designed to make non-blocking code more readable. – J D Jun 25 '12 at 17:37
  • @CarstenKönig It's not the continuation Monad (Cont), they're binding mechanisms are different and have completely different signatures. I would rather say it's based on the IO Monad. – Gus Sep 22 '12 at 13:47

1 Answers1

6

You code more or less directly can be translated to Scala using Futures (with some important features lost, though):

import scala.actors.Futures
import Futures._

val urlList = Map("Microsoft.com" -> "http://www.microsoft.com/",
                "MSDN" -> "http://msdn.microsoft.com/",
                "Bing" -> "http://www.bing.com")


def fetchAsync(name: String, url: String) = future {
    // lengthy operation simulation
    Thread.sleep(1000)
    println("Fetching from %s: %s" format(name, url))
}

def runAll = 
    //Futures.awaitAll(  <- if you want to synchronously wait for the futures to complete 
    urlList.map{case (name, url) => fetchAsync(name, url)}
    //)
Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81
  • 1
    I think the point of F# asynchronous wokflows is that you can easily write code that doesn't block threads (as it uses continuations instead of threads). I'm not that familiar with Scala, but I don't think that futures achieve this - because a future runs on a single thread until it completes, doesn't it? – Tomas Petricek Apr 07 '11 at 20:49
  • 1
    @Tomas, you're absolutely right (that's why I mentioned above that some important differences exist). A more direct analogue to F# async would be Akka's Futures implementation (http://doc.akka.io/futures-scala), or promises in Scalaz (http://stackoverflow.com/questions/2446770/how-to-split-and-dispatch-an-async-control-flow-using-continuations). – Vasil Remeniuk Apr 07 '11 at 21:53
  • 4
    @Tomas A future _is_ a continuation, and any code _must_ be run on a process or thread. At best, one can have async read and write, which is performed by some hardware and then notified to the CPU. That said, futures interact badly with blocking I/O -- because the scheduler won't spawn new threads -- which is a shame. – Daniel C. Sobral Apr 07 '11 at 22:15
  • 1
    @Daniel, I think, the biggest complaint was that in stdlib it's not possible to (easily) put a continuation on a future completion – Vasil Remeniuk Apr 08 '11 at 05:34
  • @DanielC.Sobral You missed Tomas' point. The scheduler shouldn't have to spawn new threads because the code should not be blocking the threads it already has. – J D Jun 25 '12 at 10:48
  • -1 This translation is completely wrong. The F# is non-blocking and, consequently, scalable. This Scala is not only fully blocking but it is blocking threadpool threads which will deadlock the entire threadpool. The Akka link in your comment looks relevant but its broken. – J D Jun 25 '12 at 10:51
  • 2
    @VasilRemeniuk "I think, the biggest complaint was that in stdlib it's not possible to (easily) put a continuation on a future completion". And that's the easy part. The hard part is the automated propagation of exception handlers from one continuation to the next with correct resource cleanup. – J D Jun 25 '12 at 10:52
  • @JonHarrop No, I did not miss Tomas point. What F# Async and what Scala Futures do is exactly the same thing: create tasks representing all the code it has to run, and then running all runnable tasks in an automatically managed pool of threads. Creating a future is not blocking, and there's no 1-to-1 allocation between futures and threads, just like F# Async. The only difference is that I/O on F# async return the thread, while I/O in Scala keeps the thread, unless you explicitly select async I/O. In the absence of I/O, they work just the same. – Daniel C. Sobral Jun 25 '12 at 14:34
  • @DanielC.Sobral "What F# Async and what Scala Futures do is exactly the same thing". That is incorrect. What you describe is called a `Task` on .NET. Note that the F# is non-blocking whereas the Scala is blocking... – J D Jun 25 '12 at 17:34
  • 2
    http://doc.akka.io/docs/akka/2.0.2/scala/futures.html is the updated link to the Akka futures documentation. – Gian Jun 25 '12 at 17:53
  • @JonHarrop What _exactly_ is blocking in Scala? – Daniel C. Sobral Jun 25 '12 at 19:12
  • About blocking IO: Current Scala versions have blocking { ... } with a blocking context to tell the scheduler this thread is blocking and the default implementations then adds threads to offset the blocking ones. – KingOfCoders Oct 20 '17 at 07:06