St.Antario
St.Antario

Reputation: 27375

Why can't we provide implicit typeclass when a type is covariant?

I'm designing my algebraic data type and faced a problem of unabling to provide implicit typeclass. Here is what I have:

sealed abstract class Stream[+F[_], +T] {
  def run()(implicit M: Monad[F]): F[Unit] = Stream.run(this)
}

object Stream{
  final case object Halt extends Stream[Nothing, Nothing]
  private def run[F[_], T](stream: Stream[F, T])(implicit M: Monad[F]): F[Unit] = stream match {
    case Halt => M.pure()
  }
}

I got the compile error

Error:(10, 22) covariant type F occurs in invariant position in type 
     Monad[F] of value M def run()(implicit M: Monad[F]): F[Unit] = M.pure()

I made it covariant in order to be able to return Halt extends Stream[Nothing, Nothing] without casting but yet I need Monad[F] typeclass in order to use its operation for a context F[_] provided. What would be a solution in this case?

Upvotes: 1

Views: 122

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170713

This would only compile if Monad were contra-variant. Otherwise you have:

val stream: Stream[Nothing, Nothing] = Halt
val stream1: Stream[Maybe, Int] = stream // by covariance
val x = stream1.run()(implicitly[Monad[Maybe]])

but Halt.run only accepts a Monad[Nothing], so Monad[Maybe] must be a subtype of Monad[Nothing].

What would be a solution in this case?

Not to define run like this? Generally, because scalaz and cats decided to make their typeclasses invariant, you'll run into trouble when trying to use them in co/contravariant types.

Upvotes: 3

Related Questions