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 ofNothing
Since
Nothing
has no values, and no subtypes,Empty.T
will also have no values. This lets us write our Absurd instances:This works! The following will compile:
as does:
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:
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 initialAbsurd[Empty]
instance.