Search code examples
scalahaskellconcurrencymulticorescalaz

Port Haskell code using Control.Parallel to Scala


The Haskell code below uses par and pseq to do some multicore number-crunching as a toy to show several cores being used. What would be the easiest and most idiomatic way to express this in Scala? Futures and Promises seem promising (ahem) and I have been looking at scalaz.concurrent, e.g. this example, but I can't find docs to explain it all.

import Control.Parallel

main = a `par` b `par` c `pseq` print (a + b + c)
  where
      a = ack 3 10
      b = fac 42
      c = fib 35

fac 0 = 1
fac n = n * fac (n-1)

ack 0 n = n+1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) (ack m (n-1))

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

Solution

  • You can translate your example in Scala like so:

    import concurrent.{Await, Future, future}
    import concurrent.ExecutionContext.Implicits.global
    import concurrent.duration.Duration
    
    object Main extends App {
    
      val fac: Int => Int = {
        case 0 => 1
        case n => n * fac(n-1)
      }
    
      val ack: (Int, Int) => Int = {
        case (0, n) => n + 1
        case (m, 0) => ack (m-1, 1)
        case (m, n) => ack (m-1, ack(m, n-1))
      }
    
      val fib: Int => Int = {
        case 0 => 0
        case 1 => 1
        case n => fib(n-1) + fib(n-2)
      }
    
      val fa = future { ack(3, 10) }
      val fb = future { fac(42) }
      val fc = future { fib(35) }
    
      val x = for (((a, b), c) <- fa zip fb zip fc) yield (a + b + c)
    
      val result = Await.result(x, Duration.Inf) //awaiting synchronously after the result
      println(s"Value is: $result")
    
    }
    

    The future { fib(3, 10) } bit will create an asynchronous computation which will run on a different execution thread and will return a Future object. You can then compose all your futures into one big future which will provide the list of all the results, using Future.sequence.

    We can map the result of this latter future into the sum of the results, thus obtaining the final value.

    With this final future we can do several things. We can compose it further or we can attach callbacks on it or we can wait synchronously for a specified duration of time. In my example I am waiting in a synchronous fashion after the result for an infinite period of time.