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

时间:2017-11-08 13:34:21

标签: scala functional-programming functor scalaz scala-cats

我一直在搞乱Cats / Scalaz的一些基本例子,并且还要通过教程来感受一下,我已经找到了一个我确定有解决方案的案例。

是否可以使用带有F[A]上下文的Functor视图(F[_] : Functor)调用带有上下文值(<: 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

当然,明确地召唤Functor for Option并且映射按预期工作

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

所以我的问题是:一般函数的签名是否需要更改以考虑子类上下文,是否有某种&#34;隐式扩展&#34;我不知道的,或者这只是使用stdlib在Scala中进行函数式编程的一个不幸的缺点?

2 个答案:

答案 0 :(得分:1)

// 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中的F[B]处于协变位置,F中的F[A]处于逆变位置,因此仿函数类的唯一选项是在类型构造函数中保持不变。

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

答案 1 :(得分:1)

Dmytro的答案指出,这通常不可能。这就是cat / scalaz公开.some扩展方法的原因,该方法被输入为Option,而使用Some构造函数返回Some;

takesAFunctor(1.some)

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

  

是否存在某些我不知道的“隐式扩展”,或者这只是使用stdlib在Scala中进行函数式编程的一个不幸的缺点?

手动召唤Option仿函数时看到的隐式扩展是协方差。实例是为Option不变地定义的,这就是Some不可接受的原因 - 编译器找不到隐式,但Functor[Option].map期望Option或任何子类型Option,这就是有些人工作的原因。

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