6

This perhaps been asked many times before, but none of the suggestions I've found help.

I have a simple Scala code that generates long number that depends on some side-effects. I'm wrapping thing in an IO monad, but according to the least power principle, I'm actually declaring my function as F[_]: Effect. Now code won't compile and I don't understand why, please suggest what may be wrong

import cats.effect.{Clock, Effect}
import cats.syntax.all._
import java.util.concurrent.TimeUnit


...

  def generateId[F[_]: Effect](rid: Long)(implicit F: Effect[F], clock: Clock[F]): F[Long] =
    for {
      currentTimeNanos <- clock.realTime(TimeUnit.NANOSECONDS)
      tid              <- F.delay(Thread.currentThread().getId)
    } yield
      (tid << 40 /*    */ & 0xFFFFFF0000000000L) |
        (rid << 16 /*  */ & 0x000000FFFFFF0000L) |
        (currentTimeNanos & 0x000000000000FFFFL)

[error] /.../package.scala:34:41: value flatMap is not a member of type parameter F[Long]
[error]       currentTimeNanos <- clock.realTime(TimeUnit.NANOSECONDS)
[error]                                         ^
[error] /.../package.scala:35:34: value map is not a member of type parameter F[Long]
[error]       tid              <- F.delay(Thread.currentThread().getId)

Also, if you have any suggestions on improving the code, let me know please.

edio
  • 652
  • 1
  • 8
  • 21

1 Answers1

8

The problem is that the context bound in F[_]: Effect desugars into an implicit parameter, so the compiler is seeing something like this:

def generateId[F[_]](rid: Long)(implicit ev: Effect[F], F: Effect[F], ...): F[Long] = ...

That means that every time it tries to resolve an implicit Effect[F] in the body of the method, it'll fail because it thinks the explicit F and this synthetic ev are ambiguous.

The solution is to drop either the context bound or the explicit implicit F: Effect[F] parameter. I'd suggest killing the context bound, since the fact that Scala allows you to combine the two is part of the reason it's so easy to make this kind of error (and was in my view a serious misjudgement by the language designers, as I've said many times before).

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 1
    Just for the record, if I were to use context bound, how should I get a reference to that "sugared" implicit parameter? I ended up with `implicitly[Effect[F]]`. Is this the right way to do this? – edio Feb 18 '19 at 16:22
  • 2
    @edio Yes, exactly, or just write `Effect[F]`, which is equivalent. – Travis Brown Feb 18 '19 at 16:42
  • Thank you! That sneaky `apply`... I didn't know one can call generic `apply` like that :) – edio Feb 18 '19 at 16:59
  • 1
    @edio Yeah, it's a nice trick, and a fairly common idiom that you find even in the standard library (e.g. try `Ordering[Int]`). – Travis Brown Feb 18 '19 at 17:00