0

I'm reading the book "Scala with cats". The author says that Semigroupal doesn't always provide the behaviour we expect. And he shows this example:

import cats.Semigroupal
import cats.instances.future._ // for Semigroupal
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.language.higherKinds
val futurePair = Semigroupal[Future].
product(Future("Hello"), Future(123))
Await.result(futurePair, 1.second)
// res1: (String, Int) = (Hello,123)

In the book result is (Hello,123) but we expected Future(Hello, 123). As I understood, we lost the Future, so there is no expected context.

I decided to reproduce this example and got this result:

Future(Success((Hello,123)))

Context in the place. Hmm. Then I tried this experiment:

  val futurePair = Semigroupal[Future]
    .product(Future{Thread.sleep(100);"Hello"}, Future(123))
  println(futurePair)

The result is Future(<not completed>). As I expected.

So I can't understand what's wrong with Future. I got expected behavior and I didn't lose the context of calculation. Maybe because of Future start calculation at the moment of creating? But why is it a problem?

faoxis
  • 1,912
  • 5
  • 16
  • 31
  • 3
    Who is "we" then? "We" expected `(Hello, 123)`, without `Future`. Otherwise we wouldn't have called `Await.result`. I don't think that Welsh / Gurnell claimed that they expected to get a `Future` out of `Await.result`. – Andrey Tyukin Mar 24 '19 at 12:46
  • 3
    Apparently, you took the example from page 149, section 6.3 "Semigroupal applied to different types". The only thing that is unexpected is the confusing introductory sentence *"Semigroupal doesn't always provide the behavior we expect, [...]."* After that, they go on to describe things that are exactly as expected (assuming that one expects the right things, as always). – Andrey Tyukin Mar 24 '19 at 12:53
  • What the author refers in that part of the book is that, when you use `Semigroupal` for a _type_ that also has an instance of `Monad`. All the **functions** are implemented in terms of `flatMap`. Thus, they are no longer _"parallel"_, but _"sequencial"_. - The examples with `Future` are tricky, because they are **eager**. – Luis Miguel Mejía Suárez Mar 24 '19 at 14:25

1 Answers1

0

The Await.result strips off the Future, not the product, so your expectations are matching the actual behavior here. What he is saying is that some people might expect sequential execution instead of parallel, because of how other Semigroupals behave, but he chose a poor code example, because the result of his example is the same whether executed in parallel or sequence. A better example to illustrate the author's point would be something like:

val futurePair = Semigroupal[Future].product(
  Future{Thread.sleep(750); "Hello},
  Future{Thread.sleep(750); 123}
)
Await.result(futurePair, 1.second)

The Await.result would time out if the Futures were run in sequence.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94