即使在使用类型投影后为 Either 声明 Functor 时出现编译错误

Compilation error when declaring Functor for Either even after using type projections

我正尝试在 Scala 中为 Either 编写一个 Functor 用于学术目的。在 higher-kinded 类型和 type-projections 的帮助下,我设法为 Either 编写了一个实现。

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

object Functor {
  implicit def eitherFunctor[A] = new Functor[({type λ[α] = Either[A, α]})#λ] {
    override def map[B, C](fa: Either[A, B])(f: B => C) = fa.map(f)
  }
}

def mapAll[F[_], A, B](fa: F[A])(f: A => B)(implicit fe: Functor[F]): F[B] = fe.map(fa)(f)

val right: Either[String, Int] =  Right(2)

mapAll(right)(_ + 2)

现在,上面的代码无法编译。我不确定原因,但我得到的编译错误在下面给出 -

Error:(19, 16) type mismatch;
 found   : Either[String,Int]
 required: ?F[?A]
Note that implicit conversions are not applicable because they are ambiguous:
 both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
 and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
 are possible conversion functions from Either[String,Int] to ?F[?A]
  mapAll(right)(_ + 2)

谁能指出我在上面的代码中做错了什么?

PS:请不要建议我使用kind-projector

Either[+A, +B] 需要两个 类型参数 (如@Ziyang Liu 所说),因此对于您的示例实际上需要 BiFunctor 而不是 Functor , BiFunctor 接受两个函子并绑定 两种类型

有个Bifunctor from Scalaz

trait Bifunctor[F[_, _]]  { self =>
  ////

  /** `map` over both type parameters. */
  def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D]

所以你可以像这样使用这个Bifunctor

Bifunctor[Either].bimap(Right(1): Either[String, Int])(_.toUpperCase, _ + 1).println

希望对您有所帮助。

正如其他人所说,编译器试图告诉您的是您的类型的形状不匹配。当你需要一个 F[_] 时,你需要一个带有单个类型参数的类型构造函数,Either[A, B] 不满足。

我们需要做的是在应用 mapAll 时应用类型 lambda,就像我们在创建 Either 仿函数的实例时所做的一样:

val right: Either[String, Int] = Right(2)

mapAll[({type λ[α]=Either[String, α]})#λ, Int, Int](right)(_ + 2)

我们现在压缩 String 并将其固定为第一个参数,允许类型投影类型只需要满足我们的 alpha,现在形状匹配。

当然,我们也可以使用类型别名,这将使我们在应用时无需指定任何额外的类型信息 mapAll:

type MyEither[A] = Either[String, A]
val right: MyEither[Int] = Right(2)

mapAll(right)(_ + 2)

你刚刚被 SI-2712 咬了。如果您使用的是 Scala >= 2.12.2,只需将此行添加到您的 build.sbt:

scalacOptions += "-Ypartial-unification"

对于其他 Scala 版本,您可以使用 this plugin