【问题标题】:Implicit error when trying to implement the `Absurd` typeclass尝试实现“Absurd”类型类时出现隐式错误
【发布时间】:2020-09-12 03:19:59
【问题描述】:

我正在尝试在 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]]]
}

【问题讨论】:

标签: scala typeclass implicit bottom-type


【解决方案1】:

感谢 Dmytro 的评论,我能够找到 this post suggesting a workaround 这个 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] 实例。

【讨论】:

  • 不仅implicitly[Absurd[Either[Empty.T, Empty.T]]] 编译,而且原始implicitly[Absurd[Either[Nothing, Nothing]]]
  • 这个技巧的好处是编译器知道Empty.TNothingimplicitly[Empty.T =:= Nothing], implicitly[Nothing =:= Empty.T] 编译)。与 Nothing 本身相比,编译器更不害怕推断 Empty.T
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-23
  • 1970-01-01
  • 2015-09-07
  • 2020-09-11
  • 2019-07-30
  • 1970-01-01
  • 2013-06-16
相关资源
最近更新 更多