Scalaz,*语法类的目的

Posted

技术标签:

【中文标题】Scalaz,*语法类的目的【英文标题】:Scalaz, the purpose of *syntax classes 【发布时间】:2018-11-20 08:55:28 【问题描述】:

在 scalaz 中,当我们定义一个模块时,我们额外定义了隐式的辅助函数。这是一个定义示例以及客户如何使用它:

trait Functor[F[_]] 
  def map[A,B](fa: F[A])(f: A => B): F[B]

object Functor 
  def fmap[F[_], A,B](as:F[A])(f:A=>B)
                    (implicit ff:Functor[F]):F[B] =
    ff.map(as)(f)

  implicit val listFunctor = new Functor[List] 
    def map[A,B](as: List[A])(f: A => B): List[B] = as map f
  

...
import com.savdev.NewLibrary._
val r = fmap(List(1,2))(_.toString)

final class FunctorOps[F[_], A](self: F[A])(implicit ff:Functor[F])
  def qmap[B](f:A=>B):F[B] = ff.map(self)(f)

trait ToFunctorOps 
  implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F]) =
    new FunctorOps[F,A](v)

object NewLibrary extends ToFunctorOps
...
import com.savdev.NewLibrary._
val r2 = List(1, 4) qmap (x=>x.toString)

代码略有改动。但我们的想法是:

    抽象及其 API(代数) 定义使用隐式和自身隐式的辅助泛型函数 丰富现有类型以便能够使用我们的新抽象。为此使用了隐式转换。在 scalaz 中,我们为包装器和特征中的隐式转换器定义了最终类

综上所述,它的动机以及客户如何使用它是很清楚的。但是在每个这样的模块定义的scalaz 中,还有一个相关的*Syntax 类。我无法理解它的目的。您能否解释一下,为什么需要它以及如何在客户端代码中使用它。

在 Scalaz 中定义为:

trait FunctorSyntax[F[_]] 
  implicit def ToFunctorOps[A](v: F[A]): FunctorOps[F, A] =
    new FunctorOps[F, A](v)(FunctorSyntax.this.F)
  def F: Functor[F]

更新:

伙计们,我似乎不够清楚,或者一个话题对我们所有人来说都比较复杂。

我需要了解两个特征之间的区别:

trait ToFunctorOps 
  implicit def ToFunctorOps[F[_],A](v: F[A])(implicit F0: Functor[F]) =
    new FunctorOps[F,A](v)

对比

trait FunctorSyntax[F[_]] 
  implicit def ToFunctorOps[A](v: F[A]): FunctorOps[F, A] =
    new FunctorOps[F, A](v)(FunctorSyntax.this.F)
  def F: Functor[F]

这两个特征都定义了一个创建FunctorOps 的通用方法,它们都具有相同的可见性规则。 第一个ToFunctorOps trait,它本身不是泛型的,它只用[F[_],A] 定义了泛型方法。结果,我可以将很多这样的特征组合到一个对象中,并一次导入所有这些特征。我举了一个例子,客户如何使用这些特征:

object NewLibrary extends ToFunctorOps
...
import com.savdev.NewLibrary._
val r2 = List(1, 4) qmap (x=>x.toString)

这个特性已经让客户端可以隐式地注入方法。为什么我们需要FunctorSyntax?这个 FunctorSyntax 特征本身是 [F[_]] 上的泛型。当我扩展它时,我必须在定义中提供一个类型。因为F[_]现在用在trait定义中,所以函数的泛型参数更少,只有[A]

我问你们,如果你们能帮助和理解,请给我一个代码示例,客户如何使用这个 FunctorSyntax 特征。确切地说,这还不清楚。

现在我看到试图解释其他主题,但不是原来的:

    如何创建隐式类,而不是隐式函数。 最终 *Ops 类和特征之间的区别,包括它们的可见性。在这里,我们比较了具有相同可见性的 2 个特征。 解释一般方法注入,它们如何提供帮助。 ToFunctorOps 已提供此功能。

伙计们,请再次向社区展示USE CASES via CODE of FunctorSyntax。代码本身永远是最好的文档。

最好的问候

【问题讨论】:

您在哪里看到非多态ToFunctorOps 定义?我只看到trait ToFunctorOps[TC[F[_]] <: Functor[F]] extends ToFunctorOps0[TC] with ToInvariantFunctorOps[TC] @YuvalItzchakov,我稍微简化了代码。在 scalaz v.2.11 中,它被定义为: trait ToFunctorOps 使用 ToInvariantFunctorOps 扩展了 ToFunctorOps0。但是这个 ToFunctorOps 特征本身不使用 ToFunctorOps0 或 ToInvariantFunctorOps 中的任何内容。它只是扩展它。所以我删除了这个“扩展”部分以使其更简单。 但它是多态的吗? 【参考方案1】:

从我在 scalaz 代码库中看到的内容来看,我认为 FunctorSyntax 是一种启用语法的替代方式。他们这样定义Functor(简化):

trait Functor 
  def map[A, B](fa: F[A])(f: A => B): F[B]

  val functorSyntax = new FunctorSyntax[F]  def F = Functor.this 

这启用了以下工作方式:

def foo[F[_]](f: F[String])(implicit F: Functor[F]): F[Int] = 
  import F.functorSyntax._
  f.map(_.length)

对比ToFunctorOps 添加语法的方式:

package scalaz.syntax  // simplified version of the scalaz codebase
  object functor extends ToFunctorOps 


import scalaz.syntax.functor._
def foo[F[_]: Functor](f: F[String]): F[Int] = f.map(_.length)

【讨论】:

但谁真正使用F.functorSyntax._?您是在内部使用 scalaz 吗?因为大多数时候你只是去import scalaz.syntax.functor._ 我不知道他们内部如何使用它。但这是他们在Functor 定义中包含val functorSyntax = new FunctorSyntax[F] def F = Functor.this 的唯一原因。实际上 Scala 标准库类型类使用相同的模式。您可以在import Ordering.Implicits._def foo[A](implicit ord: Ordering[A]) = import ord._; ... 之间进行选择。【参考方案2】:

这是一个使用 functorSyntax 的用例:

import org.scalatest.FreeSpec, Matchers

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await, Future
import scala.concurrent.duration._
import scalaz._
import Scalaz._

import scala.language.postfixOps

class ScalazTest extends FreeSpec with Matchers 
  "compose functors" in 
    val composedFunctor = Functor[Future] compose Functor[List] compose Functor[Option]
    import composedFunctor.functorSyntax._

    val actual = Future.successful(List(Some(1), Some(2), None, Some(4))) fmap (x => x * 2)
    Await.result(actual, 10 seconds) shouldBe List(Some(2), Some(4), None, Some(8))
  

这个想法是,您可以组合多个仿函数实例并将最终组合仿函数的实例导入范围并使用它。请注意,在这种情况下,fmap 被解析为 composedFunctor.functorSyntax,它适用于 3 层嵌套 (Future[List[Option[Integer]]]),同时仍接受处理原始类型的函数。

【讨论】:

以上是关于Scalaz,*语法类的目的的主要内容,如果未能解决你的问题,请参考以下文章

Part7 继承与派生 7.1继承的基本概念和语法 7.2 继承方式

Akka(27): Stream:Use case-Connecting Slick-dbStream & Scalaz-stream-fs2

Java基础语法05-面向对象中

Java语法包 继承 多态 抽象类 接口

C++基本语法

FunDA- 流动数据行操作:FDAPipeLine operations using scalaz-stream-fs2