尝试实现“荒谬”类型类时出现隐式错误
Implicit error when trying to implement the `Absurd` typeclass
我正在尝试在 Scala 中实现 Absurd
typeclass (as seen in Haskell's Data.Boring library)。
我可以为 Nothing
定义一个 Absurd
实例。
不幸的是,当我尝试为 Either
定义一个荒谬的实例时,我得到了一个丢失的隐式错误
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)
}
}
}
这样编译:
implicitly[Absurd[Nothing]]
编译失败:
implicitly[Absurd[Either[Nothing, Nothing]]]
我正在使用 Scala 版本“2.13.2”。
可能有趣的是,以下非常相似的代码(不涉及 Nothing
)确实可以编译:
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]]]
}
感谢 Dmytro 的评论,我能够找到 this post suggesting a workaround for this bug。
简而言之,我们可以为Nothing
的子类型定义一个类型别名Empty.T
object Empty{
type T <: Nothing
}
因为 Nothing
没有值,也没有子类型,所以 Empty.T
也没有值。这让我们可以编写我们的 Absurd 实例:
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)
}
}
}
这行得通!以下将编译:
implicitly[Absurd[Either[Empty.T, Empty.T]]]
一样:
implicitly[Absurd[Either[Nothing, Nothing]]]
因为我正在移植 Haskell 代码,它不必担心差异,定义我们自己的空类型作为解决方法同样有效:
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 = ???
}
// ...
}
这行得通,但我个人更喜欢第一种方法,因为它不会忽略已经内置到 Scala 中的 Empty 类型 Nothing
,而且它不依赖于我们使用 ???
写入初始 Absurd[Empty]
实例。
我正在尝试在 Scala 中实现 Absurd
typeclass (as seen in Haskell's Data.Boring library)。
我可以为 Nothing
定义一个 Absurd
实例。
不幸的是,当我尝试为 Either
定义一个荒谬的实例时,我得到了一个丢失的隐式错误
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)
}
}
}
这样编译:
implicitly[Absurd[Nothing]]
编译失败:
implicitly[Absurd[Either[Nothing, Nothing]]]
我正在使用 Scala 版本“2.13.2”。
可能有趣的是,以下非常相似的代码(不涉及 Nothing
)确实可以编译:
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]]]
}
感谢 Dmytro 的评论,我能够找到 this post suggesting a workaround for this bug。
简而言之,我们可以为Nothing
Empty.T
object Empty{
type T <: Nothing
}
因为 Nothing
没有值,也没有子类型,所以 Empty.T
也没有值。这让我们可以编写我们的 Absurd 实例:
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)
}
}
}
这行得通!以下将编译:
implicitly[Absurd[Either[Empty.T, Empty.T]]]
一样:
implicitly[Absurd[Either[Nothing, Nothing]]]
因为我正在移植 Haskell 代码,它不必担心差异,定义我们自己的空类型作为解决方法同样有效:
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 = ???
}
// ...
}
这行得通,但我个人更喜欢第一种方法,因为它不会忽略已经内置到 Scala 中的 Empty 类型 Nothing
,而且它不依赖于我们使用 ???
写入初始 Absurd[Empty]
实例。