尝试实现“荒谬”类型类时出现隐式错误

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] 实例。