使用子类使用 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 不变类型类型之间的阻抗不匹配
我一直在研究 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 不变类型类型之间的阻抗不匹配