无法恢复类型为 F[A1, A2] 且具有语法扩展的类型的类型实例
Unable to recover typeclass instances for types with kind F[A1, A2] with syntax extensions
我一直在研究 Scala 中的 typeclass 模式以更好地理解它是如何工作的,因为我熟悉 Scalaz 并想弄清楚它是如何工作的 "under the hood"。
(您可以 运行 使用 https://ammonite.io/ REPL 编写以下代码)
import $plugin.$ivy.`org.spire-math::kind-projector:0.9.3`
sealed trait Maybe[+A] // Option
final case class Just[+A](value: A) extends Maybe[A]
final case object Null extends Maybe[Nothing]
sealed trait Direction[+E, +A] // Either
final case class Up[+E, +A](value: A) extends Direction[E, A]
final case class Down[+E, +A](value: E) extends Direction[E, A]
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
object FunctorSyntax {
implicit final class FunctorExtensions[F[_], A](private val self: F[A]) extends AnyVal {
def map[B](f: A => B)(implicit instance: Functor[F]): F[B] = {
instance.map(self)(f)
}
}
}
object FunctorInstances {
implicit val maybeFunctorInstance: Functor[Maybe] = new Functor[Maybe] {
def map[A, B](fa: Maybe[A])(f: A => B): Maybe[B] = fa match {
case Just(a) => Just(f(a))
case n@Null => n
}
}
implicit def directionFunctorInstance[E]: Functor[Direction[E, ?]] = new Functor[Direction[E, ?]] {
def map[A, B](fa: Direction[E, A])(f: A => B): Direction[E, B] = fa match {
case Up(a) => Up(f(a))
case Down(e) => Down(e)
}
}
}
所以我写了一些类似于 Option
(Maybe
) 和 Either
(Direction
) 的 Functor
定义,一些实例Functor
,一些语法扩展,这样我就可以在有效的仿函数上调用 .map
。
以下代码有效:
import FunctorInstances._
import FunctorSyntax._
val x: Maybe[Int] = Just(5)
println(x.map(_ + 1)) // prints "Just(6)"
符合预期。但以下不是:
val y: Direction[String, Int] = Up(5)
println(y.map(_ + 1)) // errors out
抛出错误 help.sc:48: value map is not a member of ammonite.$file.help.Direction[String,Int]
简单地说,我不希望这个错误发生,并且 .map
可以在任意 Direction[E, ?]
上工作。
我认为 Scala 无法将 Direction[String, Int]
解构为 F = Direction[String, ?]
和 A = String
,从而阻止了 FunctorExtensions
class 将自身包裹在 val y: Direction[String, Int]
周围。不幸的是,我不知道如何解决这个问题。
注意:实例本身仍然可以通过 implicitly
恢复
val instance = implicitly[Functor[Direction[String, ?]]]
println(instance.map(y)(_ + 1)) // prints "Up(6)"
如果您使用的是 Scala 2.11 或 Scala 2.12,您可能缺少 -Ypartial-unification
scalac 选项。
将 scalacOptions += "-Ypartial-unification"
添加到您的构建中,或者您可以使用默认启用它的 Scala 2.13。
见https://github.com/typelevel/cats/blob/v1.6.1/README.md#getting-started
这对我来说适用于以下设置:
Welcome to the Ammonite Repl 1.6.3
(Scala 2.12.8 Java 1.8.0_141)
我一直在研究 Scala 中的 typeclass 模式以更好地理解它是如何工作的,因为我熟悉 Scalaz 并想弄清楚它是如何工作的 "under the hood"。
(您可以 运行 使用 https://ammonite.io/ REPL 编写以下代码)
import $plugin.$ivy.`org.spire-math::kind-projector:0.9.3`
sealed trait Maybe[+A] // Option
final case class Just[+A](value: A) extends Maybe[A]
final case object Null extends Maybe[Nothing]
sealed trait Direction[+E, +A] // Either
final case class Up[+E, +A](value: A) extends Direction[E, A]
final case class Down[+E, +A](value: E) extends Direction[E, A]
trait Functor[F[_]] {
def map[A, B](fa: F[A])(f: A => B): F[B]
}
object FunctorSyntax {
implicit final class FunctorExtensions[F[_], A](private val self: F[A]) extends AnyVal {
def map[B](f: A => B)(implicit instance: Functor[F]): F[B] = {
instance.map(self)(f)
}
}
}
object FunctorInstances {
implicit val maybeFunctorInstance: Functor[Maybe] = new Functor[Maybe] {
def map[A, B](fa: Maybe[A])(f: A => B): Maybe[B] = fa match {
case Just(a) => Just(f(a))
case n@Null => n
}
}
implicit def directionFunctorInstance[E]: Functor[Direction[E, ?]] = new Functor[Direction[E, ?]] {
def map[A, B](fa: Direction[E, A])(f: A => B): Direction[E, B] = fa match {
case Up(a) => Up(f(a))
case Down(e) => Down(e)
}
}
}
所以我写了一些类似于 Option
(Maybe
) 和 Either
(Direction
) 的 Functor
定义,一些实例Functor
,一些语法扩展,这样我就可以在有效的仿函数上调用 .map
。
以下代码有效:
import FunctorInstances._
import FunctorSyntax._
val x: Maybe[Int] = Just(5)
println(x.map(_ + 1)) // prints "Just(6)"
符合预期。但以下不是:
val y: Direction[String, Int] = Up(5)
println(y.map(_ + 1)) // errors out
抛出错误 help.sc:48: value map is not a member of ammonite.$file.help.Direction[String,Int]
简单地说,我不希望这个错误发生,并且 .map
可以在任意 Direction[E, ?]
上工作。
我认为 Scala 无法将 Direction[String, Int]
解构为 F = Direction[String, ?]
和 A = String
,从而阻止了 FunctorExtensions
class 将自身包裹在 val y: Direction[String, Int]
周围。不幸的是,我不知道如何解决这个问题。
注意:实例本身仍然可以通过 implicitly
val instance = implicitly[Functor[Direction[String, ?]]]
println(instance.map(y)(_ + 1)) // prints "Up(6)"
如果您使用的是 Scala 2.11 或 Scala 2.12,您可能缺少 -Ypartial-unification
scalac 选项。
将 scalacOptions += "-Ypartial-unification"
添加到您的构建中,或者您可以使用默认启用它的 Scala 2.13。
见https://github.com/typelevel/cats/blob/v1.6.1/README.md#getting-started
这对我来说适用于以下设置:
Welcome to the Ammonite Repl 1.6.3
(Scala 2.12.8 Java 1.8.0_141)