Search code examples
scalatypeclassimplicitscala-catscats-effect

Make F[_] implementing Monad trait while accepting IO


Let us say we have the code (<: Monad[F] doesn't work as expected):

class External[F[_] <: Monad[F] : Concurrent](implicit proxy: Proxy[F]) { ... }

class Proxy[F[_] <: Monad[F]](implicit storage: Storage, async: Async[F]) {
  def get(key: String): F[Option[Entry]] = {
    async.blocking(storage.get(key))
  }
}

I would like F[_] to be a Monad, so that proxy.get() have those traits and enables for example (inside External class):

proxy.get(key).flatMap(...)

So far so good, but when trying to instantiate with cats.effect.IO it doesn't work for External:

implicit val proxy: Proxy[IO] = new Proxy()
implicit val external: External[IO] = new External()

With error output:

inferred type arguments [[+A]cats.effect.IO[A]] do not conform to value <local External>'s type parameter bounds [F[_] <: cats.Monad[F]]

How can this be fixed or achieved in a different way?


Solution

  • Replace

    class External[F[_] <: Monad[F] : Concurrent]
    

    with

    class External[F[_]: Monad : Concurrent]
    

    Being a Monad doesn't mean being a subtype of Monad. It means there is an instance of the type class Monad for current type.

    On contrary to OOP, in FP implementing some abstract behavior is achieved not with extending/inheritance/subtype polymorphism but with implicits/defining type-class instances/ad hoc polymorphism.

    Maybe you need to import necessary syntax:

    import cats.syntax.flatMap._
    

    or

    import cats.syntax.functor._
    

    or all synaxes at once

    import cats.syntax.all._
    

    How to enforce F[_] to be an instance of Monad

    https://eed3si9n.com/herding-cats/import-guide.html