bbarrington (25) [Avatar] Offline
#1
I have a question about the 'delay' function presented on p. 134.
def delay[A](fa: => Par[A]): Par[A] =
es => fa(es)


The text says: "This is still a useful combinator [ ... ], since it lets us delay instantiation of a parallel computation until it is actually needed."

I'm confused because Par[A] is a function ( es => Future[A] ), so isn't it's "instantiation" already delayed? 'delay' returns a function that invokes this Par[A] in its body. How does this help?

Maybe an example showing the motivation for this would clarify things.
jesse (1) [Avatar] Offline
#2
Par[A] is a function ( es => Future[A] ), so isn't it's "instantiation" already delayed?

The text states that delay is useful because it allows us to delay the instantiation of a "computation". Here, "computation" refers to a Par instance itself, not to the Future[A] produced by the Par instance.

delay is useful because creating an instance of Par may itself be an expensive operation. delay allows you to say, "here's some chunk of code that will create a new Par instance. Don't execute this code yet." Consider the following equivalent implementation of delay:
def delay[A](fa: => Par[A]): Par[A] = {
  val placeholder: Par[A] = es => fa(es)
  placeholder
}

In exchange for a Par-creating chunk of code (fa), delay gives you a new, immediately-available Par instance that can act as a "placeholder" for your original Par. You can use this placeholder in other combinators, pass it to other functions by-value, etc., and you won't incur the cost of constructing your original Par until you decide you actually need to execute it.

If and when you actually need to execute your original Par, you supply the "placeholder" Par with an ExecutorService and two things happen: first, the code that describes how to create the original Par is evaluated; second, the original Par is evaluated using the provided ExecutorService. In other words, this:
val placeholder = Par.delay {
  println("Creating Par instance")
  Par.unit(42)
}
println("Providing executor service")
println(placeholder(someExecutor).get)

results in:

Providing executor service
Creating Par instance
42

Whereas this:
val par = {
  println("Creating Par instance")
  Par.unit(42)
}
println("Providing executor service")
println(par(someExecutor).get)

results in:

Creating Par instance
Providing executor service
42