maks
maks

Reputation: 6006

Issue with generic type bounds

I have a following trait:

trait Recoverable[M[_]] {
    def recoverW[T, U >: T](f: PartialFunction[Throwable, M[U]])(implicit executor: ExecutionContext): M[U]
    def fail[T](error: Throwable): M[T]
}

Also I have an implicit conversion method in the same package:

implicit def tryRecoverable[T](`try`: Try[T]) = new Recoverable[Try] {
    override def recoverW[T, U >: T](f: PartialFunction[Throwable, Try[U]])(implicit executor: ExecutionContext): Try[U] = `try`.recoverWith[U](f)
    override def fail[T](error: Throwable): Try[T] = Failure(error)
  }

This code doesn't compile complaining with

type arguments [U] do not conform to method recoverWith's type parameter bounds [U >: T]
    override def recoverW[T, U >: T](f: PartialFunction[Throwable, Try[U]])(implicit executor: ExecutionContext): Try[U] = `try`.recoverWith[U](f)
                                                                                                                                            ^

Why this code doesn't compile?

Upvotes: 0

Views: 68

Answers (1)

dk14
dk14

Reputation: 22374

Because T inside recoverW and T inside tryRecoverable - are different T's ([T, ...] defines new method-scoped T), you may:

trait Recoverable[M[_]] {
    type TT
    def recoverW[U >: TT](f: PartialFunction[Throwable, M[U]])(implicit executor: ExecutionContext): M[U]
    def fail(error: Throwable): M[TT]
}

implicit def tryRecoverable[T](`try`: Try[T]) = new Recoverable[Try] {
    type TT = T
    override def recoverW[U >: T](f: PartialFunction[Throwable, Try[U]])(implicit executor: ExecutionContext): Try[U] = `try`.recoverWith[U](f)
    override def fail(error: Throwable): Try[T] = Failure(error)
}

But i wouldn't use M[_] definition here, as it's mostly used when you can actually concretise your M[_] inside your method, like:

trait Monad[M[_]] extends Applicative[M] {
  def flatMap[T, U](m: M[T])(fn: (T) => M[U]): M[U]
  ...
}

See Twitter's Monad for instance. Actually, using M[_] is more Haskell-style as there is no classess, so monad itself is passed as parameter to method. So maybe you shoudn't use it at all and specify trait Recoverable[M[T]] directly.

Or you can do it Haskell style:

trait Recoverable[M[_]] {
    def recoverW[T, U >: T](m: M[T])(f: PartialFunction[Throwable, M[U]])(implicit executor: ExecutionContext): M[U]
    def fail[T](m: M[T])(error: Throwable): M[T]
}   

implicit val tryRecoverable = new Recoverable[Try] {
    override def recoverW[T, U >: T](`try`: Try[T])( f: PartialFunction[Throwable, Try[U]])(implicit executor: ExecutionContext): Try[U] = `try`.recoverWith[U](f)
    override def fail[T](`try`: Try[T])( error: Throwable): Try[T] = Failure(error)
 }

Then use it as:

 scala> implicitly[Recoverable[Try]].recoverW(Try{"aaaa"})({case x => Try{"bbb"}})
 res54: scala.util.Try[String] = Success(aaaa)

Or just require it inside your function/class using Try[T]: Recoverable syntax sugar:

 def recover[T, Try[T]: Recoverable](t: Try[T]) = recoverW(t) _

Upvotes: 2

Related Questions