使用子类使用 Functor 调用泛型函数 (cats/scalaz)

Calling generic function with Functor using subclass (cats/scalaz)

我一直在研究 Cats/Scalaz 的一些基本示例,还浏览了教程以了解情况,我遇到了一个案例,我确信有解决方案。

是否可以调用具有上下文 <: F 的函子视图 (F[_] : Functor) 的通用函数,该函数采用上下文化值 (F[A])?我知道 Functor 在类型 F[_] 上是不变的,而且我也知道 Functor.widen 的存在,但似乎很奇怪没有办法隐式地扩大我的类型以用于一般功能。

Cats 中的示例(也存在与 Scalaz 的类似示例):

import cats.instances.all._
import cats.syntax.all._

def takesAFunctor[F[_] : cats.Functor](f: F[Int]) = f.map(_ + 1)

takesAFunctor(Option(1)) // Works fine (of course)
takesAFunctor(Some(1)) // No implicit for Functor[Some]. Makes sense, but can we summon one since we have a Functor[Option]?
takesAFunctor(Some(1): Option[Int]) // Works but very verbose

当然,显式调用 Functor for Option 并且映射按预期工作

Functor[Option].map(Some(1))(_ + 1) // Some(2)

所以我的问题是:是否需要更改通用函数的签名以说明子类上下文,是否存在某种我不知道的 "implicit widening",或者这只是一个使用 stdlib 在 Scala 中进行函数式编程的不幸缺点?

// No implicit for Functor[Some]. 
// Makes sense, but can we summon one since we have a Functor[Option]?

一般情况下你会如何定义这样的实例?

  implicit def subtypeFunctor[F[_], G[T] <: F[T]](implicit functor: Functor[F]): Functor[G] = new Functor[G] {
    override def map[A, B](ga: G[A])(f: A => B): G[B] = functor.map(ga)(f)
  }

不起作用,因为 functor.map(ga)(f) 通常是 F[B] 类型,不一定是 G[B].

所以通常不会,不可能为子类型构造函数派生仿函数,原因很根本。

Functor F 将对象 T 映射到对象 F[T] 并将态射 f: A => B 映射到态射 map(f): F[A] => F[B] (加上一些定律)。 F in F[B] 处于协变位置,F in F[A] 处于逆变位置,因此函子类型 class 的唯一选择是类型不变构造函数。

顺便说一下,您也可以将 takesAFunctor 称为 takesAFunctor[Option](Some(1))

正如 Dmytro 的回答所指出的,这通常是不可能的。这就是 cats/scalaz 公开 .some 扩展方法的原因,该扩展方法类型为返回 Option,而使用 Some 构造函数 returns Some

takesAFunctor(1.some)

或者您可以使用更通用的 Apply 语法; takesAFunctor(1.pure[Option])

is there some sort of "implicit widening" that I don't know about, or is this just an unfortunate drawback to functional programming in Scala using the stdlib?

您在手动调用 Option 函子时看到的隐式加宽是协方差。该实例始终为 Option 定义,这就是为什么 Some 不可接受的原因 - 编译器无法找到隐式,但 Functor[Option].map 需要 Option 或任何子类型Option,这就是 Some 有效的原因。

你在这里提到的缺点基本上是 java-ish 协变子类型和更多 haskell-ish 不变类型类型之间的阻抗不匹配