问题
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]]]
}
回答1:
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.
来源:https://stackoverflow.com/questions/62003214/implicit-error-when-trying-to-implement-the-absurd-typeclass