我一直在搞乱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中进行函数式编程的一个不幸的缺点?
答案 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不变类型类型之间的阻抗不匹配