Implicit error when trying to implement the `Absurd` typeclass

╄→尐↘猪︶ㄣ 提交于 2020-06-25 04:12:24

问题


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]]]
}

回答1:


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 of Nothing

object Empty{
  type T <: Nothing
}

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]]]

as does:

implicitly[Absurd[Either[Nothing, Nothing]]]

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 = ???
  }
  // ...
}

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.



来源:https://stackoverflow.com/questions/62003214/implicit-error-when-trying-to-implement-the-absurd-typeclass

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!