Thursday, May 23, 2013

Even more reasons not to use Scala

In a response to my post to Quora, where I recommended against use of Scala for any serious project, a reader posted this:

Let's say you have three functions (f,g, and h) that receive an integer and performs an asynchronous computation, returning Future of Int and you need to chain the three functions together (use the result of one computation as input to the next one). In Scala you'll do: 

 f(x).flatMap(g(_).flatMap(h(_)))


 or with the "for" notation:  


 for {  
    i <- f(x)  
    ii <- g(i)  
    iii <-h(ii)  
    } yield iii  

What appears to be a cleaver (or "cute") use of monadic composition is actually seems to be completely misleading.   A closer look at the "flatMap" implementation in Future shows that:


 def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = {  
   val p = Promise[S]()  
   onComplete {  
    case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]  
    case Success(v) =>  
     try {  
      f(v).onComplete({  
       case f: Failure[_] => p complete f.asInstanceOf[Failure[S]]  
       case Success(v) => p success v  
      })(internalExecutor)  
     } catch {  
      case NonFatal(t) => p failure t  
     }  
   }(executor)  
   p.future  
  }  

Another word, your set of futures are no longer running independently and asynchronously (as you would expect in Future)  they are composed sequentially in a single Future.   If that was your original goal, then you should have composed the functions (in this case f, g, h) and run them in a Future.   On the other hand when you do:
f(x).flatMap(g(_).flatMap(h(_))) 
you must be thinking that the functions are running in parallel and working on each others output.    But it seems (reading the code and not actually done a test, as I don't have the Scala dev environment handy)  to me that "g" would not run at all until "f" is finished.   Again not what you would expect when you are using Futures.


No comments: