问题描述
我正在尝试实现 Absurd
类型类(在Haskell的Data.Boring库中看到)在Scala中.
I'm trying to implement the Absurd
typeclass (as seen in Haskell's Data.Boring library) in Scala.
我可以为Nothing
定义一个Absurd
实例.不幸的是,当我尝试为Either
定义一个荒谬的实例时,我得到了一个丢失的隐式错误
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)
}
}
}
它将编译:
implicitly[Absurd[Nothing]]
无法编译:
implicitly[Absurd[Either[Nothing, Nothing]]]
我正在使用Scala版本"2.13.2".
I'm using Scala Version "2.13.2".
可能有意思的是,以下非常相似的代码(不涉及Nothing
)可以编译:
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]]]
}
推荐答案
由于Dmytro的评论,我得以找到此文章建议针对此 .
Thanks to Dmytro's comment, I was able to find this post suggesting a workaround for this bug.
简而言之,我们可以为Nothing
In short, we can define a type alias Empty.T
for subtypes of Nothing
object Empty{
type T <: Nothing
}
由于Nothing
没有值,也没有子类型,因此Empty.T
也将没有值.这使我们可以编写荒诞的实例:
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]]]
与之一样:
implicitly[Absurd[Either[Nothing, Nothing]]]
由于我正在移植Haskell代码,而不必担心差异,因此将我们自己的空类型定义为解决方法同样有效:
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 = ???
}
// ...
}
这可行,但是我个人更喜欢第一种方法,因为它不会忽略Scala中已经内置的Empty类型Nothing
,并且它不依赖我们使用???
来写初始Absurd[Empty]
实例.
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.
这篇关于尝试实现`Absurd`类型类时的隐式错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!