简体   繁体   中英

Implicit error when trying to implement the `Absurd` typeclass

I'm trying to implement the Absurd typeclass (as seen in Haskell's Data.Boring library) in Scala.

I'm able to define an Absurd instance for Nothing . Unfortunately, when I try to define an absurd instance for Either , I get a missing implicit error

sealed trait Absurd[A] {
    def absurd[X](a: A): X
}

object Absurd {
  def apply[A: Absurd, B](a: A):B = implicitly[Absurd[A]].absurd[B](a)

  implicit val absurdForNothing: Absurd[Nothing] = new Absurd[Nothing]{
    override def absurd[X](a: Nothing): X = a
  }

  implicit def absurdForEither[A: Absurd, B: Absurd]: Absurd[Either[A, B]] = new Absurd[Either[A, B]]{
    override def absurd[X](a: Either[A,B]): X = a match {
      case Left(a) => Absurd[A, X](a)
      case Right(b) => Absurd[B, X](b)
    }
  }
}

This compiles:

implicitly[Absurd[Nothing]]

This fails to compile:

implicitly[Absurd[Either[Nothing, Nothing]]]

I'm using Scala Version "2.13.2".

It is possibly intersting to note, that the following, very similar code (which doesn't involve Nothing ), does compile:

trait SomeTypeclass[A]
case class SomeInstance()

object SomeTypeclass {
  implicit val someTypeclassForSomeInstance: SomeTypeclass[SomeInstance] = new SomeTypeclass[SomeInstance] {}

  implicit def someTypeclassForEither[A: SomeTypeclass, B: SomeTypeclass]: SomeTypeclass[Either[A, B]] = new SomeTypeclass[Either[A, B]] {}
}
object SomeApplicationCode {
  implicitly[SomeTypeclass[Either[SomeInstance, SomeInstance]]]
}

Thanks to Dmytro's comment, I was able to find this post suggesting a workaround for this bug .

In short, we can define a type alias Empty.T for subtypes of Nothing

object Empty{
  type T <: Nothing
}

Since Nothing has no values, and no subtypes, Empty.T will also have no values. This lets us write our Absurd instances:

object Absurd {
  def apply[A: Absurd, B](a: A):B = implicitly[Absurd[A]].absurd[B](a)

  implicit val absurdForEmptyT: Absurd[Empty.T] = new Absurd[Empty.T]{
    override def absurd[X](a: Empty.T): X = a
  }

  implicit def absurdForEither[A:Absurd, B: Absurd]: Absurd[Either[A, B]] = new Absurd[Either[A, B]]{
    override def absurd[X](a: Either[A,B]): X = a match {
      case Left(a) => Absurd[A,X](a)
      case Right(b) => Absurd[B, X](b)
    }
  }
}

This works: The following will compile:

implicitly[Absurd[Either[Empty.T, Empty.T]]]

as does:

implicitly[Absurd[Either[Nothing, Nothing]]]

Since I'm porting Haskell code, which doesn't have to worry about variance, it would be equally valid to define our own empty type as a workaround:

sealed trait Empty

object Absurd {
  def apply[A: Absurd, B](a: A):B = implicitly[Absurd[A]].absurd[B](a)

  implicit val absurdForEmpty: Absurd[Empty] = new Absurd[Empty]{
    override def absurd[X](a: Empty): X = ???
  }
  // ...
}

This works, but personally I prefer the first approach, since it doesn't ignore the Empty type Nothing that's already built into Scala, and since It doesn't rely on us to use ??? to write the initial Absurd[Empty] instance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM