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



我一直在弄乱一些 Cats/Scalaz 的基本示例,并且还浏览了教程以获得感觉,我遇到了一个案例,我相信有一个解决方案。

是否可以调用一个广义函数,该函数采用具有函子视图(F[_] : Functor)的上下文化值(F[A]),上下文<: F?我知道 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

当然,显式调用 Option 的函子并映射按预期工作

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

所以我的问题是:通用函数的签名是否需要更改以考虑子类化上下文,是否存在某种我不知道的"隐式加宽",或者这只是使用 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].

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

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

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

正如Dmytro的回答所指出的那样,这通常是不可能的。这就是为什么 cats/scalaz 公开了一个类型为返回Option.some扩展方法,而使用Some构造函数返回Some;

takesAFunctor(1.some)

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

是否有某种我不知道的"隐式加宽",或者这只是使用 stdlib 在 Scala 中进行函数式编程的一个不幸缺点?

手动召唤 Option 函子时看到的隐式加宽是协方差。该实例是为Option始终定义的,这就是为什么Some是不可接受的 - 编译器找不到隐式,但Functor[Option].map期望Option或任何子类型的Option,这就是 有些工作的原因。

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

相关内容

  • 没有找到相关文章

最新更新