1
def map2[A,B,C] (a: Par[A], b: Par[B]) (f: (A,B) => C) : Par[C] =
(es: ExecutorService) => {
  val af = a (es)
  val bf = b (es)
  UnitFuture (f(af.get, bf.get))
}

def map3[A,B,C,D] (pa :Par[A], pb: Par[B], pc: Par[C]) (f: (A,B,C) => D) :Par[D]  =
map2(map2(pa,pb)((a,b)=>(c:C)=>f(a,b,c)),pc)(_(_))

I have map2 and need to produce map3 in terms of map2. I found the solution in GitHub but it is hard to understand. Can anyone put a sight on it and explain map3 and also what this does (())?

2 Answers 2

3

On a purely abstract level, map2 means you can run two tasks in parallel, and that is a new task in itself. The implementation provided for map3 is: run in parallel (the task that consist in running in parallel the two first ones) and (the third task).

Now down to the code: first, let's give name to all the objects created (I also extended _ notations for clarity):

def map3[A,B,C,D] (pa :Par[A], pb: Par[B], pc: Par[C]) (f: (A,B,C) => D) :Par[D]  = {
  def partialCurry(a: A, b: B)(c: C): D = f(a, b, c)
  val pc2d: Par[C => D] = map2(pa, pb)((a, b) => partialCurry(a, b))
  def applyFunc(func: C => D, c: C): D = func(c)
  map2(pc2d, pc)((c2d, c) => applyFunc(c2d, c)
}

Now remember that map2 takes two Par[_], and a function to combine the eventual values, to get a Par[_] of the result.

The first time you use map2 (the inside one), you parallelize the first two tasks, and combine them into a function. Indeed, using f, if you have a value of type A and a value of type B, you just need a value of type C to build one of type D, so this exactly means that partialCurry(a, b) is a function of type C => D (partialCurry itself is of type (A, B) => C => D). Now you have again two values of type Par[_], so you can again map2 on them, and there is only one natural way to combine them to get the final value.

Sign up to request clarification or add additional context in comments.

Comments

0

The previous answer is correct but I found it easier to think about like this:

  def map3[A, B, C, D](a: Par[A], b: Par[B], c: Par[C])(f: (A, B, C) => D): Par[D] = {
    val f1 = (a: A, b: B) => (c: C) => f(a, b, c)
    val f2: Par[C => D] = map2(a, b)(f1)
    map2(f2, c)((f3: C => D, c: C) => f3(c))
  }

Create a function f1 that is a version of f with the first 2 arguments partially applied, then we can map2 that with a and b to give us a function of type C => D in the Par context (f1).

Finally we can use f2 and c as arguments to map2 then apply f3(C => D) to c to give us a D in the Par context.

Hope this helps someone!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.