【问题标题】:Calling generic function with Functor using subclass (cats/scalaz)使用子类 (cats/scalaz) 使用 Functor 调用泛型函数
【发布时间】:2018-04-21 05:21:04
【问题描述】:

我一直在研究 Cats/Scalaz 的一些基本示例,还通过教程来感受一下,我遇到了一个我确信有解决方案的案例。

是否可以使用具有上下文为<: F 的 Functor 视图 (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

当然,为 Option 显式调用 Functor 并按预期进行映射

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

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

【问题讨论】:

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


    【解决方案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 =&gt; B 到态射 map(f): F[A] =&gt; F[B](加上一些定律)。 F[B] 中的 F 处于协变位置,F[A] 中的 F 处于逆变位置,因此函子类型类的唯一选择是在类型构造函数中保持不变。

    对了,你也可以把takesAFunctor叫成takesAFunctor[Option](Some(1))。

    【讨论】:

    • 这是一个很好的细分,谢谢。我想我理解了为什么 Functor 现在在其上下文中是不变的背后的概念,这肯定有助于解释为什么我尝试的一切都不起作用!
    【解决方案2】:

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

    takesAFunctor(1.some)

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

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

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

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

    【讨论】:

    • 啊哈!这正是我想要的。谢谢!我完全忽略了考虑函数方差,这很有意义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 2019-09-22
    • 1970-01-01
    相关资源
    最近更新 更多