Scalaz,*语法类

时间:2018-06-11 07:06:22

标签: scala design-patterns scalaz

在我们定义模块的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)

代码稍有改动。但我们的想法是我们定义:

  1. 抽象及其API(代数)
  2. 定义使用implicits并暗示自己的辅助通用函数
  3. 丰富现有类型以便能够使用我们的新抽象。隐式转换用于此。在scalaz中,我们为特征中的包装器和隐式转换器定义了最终类
  4. 以上所述,它的动机以及客户如何使用它是显而易见的。但是在每个这样的模块定义的scalaz中,还有一个相关的*Syntax类。我无法理解它的目的。您能否请求exlain,为什么需要它以及如何在客户端代码中使用它。

    在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)
    }
    

    VS

    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特征,它本身不是通用的,它只定义了[F[_],A]的通用方法。结果,我可以将很多这样的特征组合成一个对象并一次导入所有这些特征。我举了一个例子,说明客户可以使用这些特征:

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

    此特征已经为客户提供了隐式注入方法的可能性。我们为什么需要FunctorSyntax?这个FunctorSyntax特征在[F[_]]上是一个通用的。当我扩展它时,我必须在定义中提供一个类型。由于F[_]现在用于特征定义,因此函数的通用参数较少,只有[A]

    我问你们,如果你们可以提供帮助和解决,请给我一个代码示例,说明客户可以使用这个FunctorSyntax特征。究竟这还不清楚。

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

    1. 如何创建隐式类,而不是隐式函数。
    2. 最终* Ops类与特征之间的差异,包括其可见性。在这里,我们比较两个具有相同可见性的特征。
    3. 解释一般方法注射,它们如何帮助。此功能已在ToFunctorOps
    4. 中提供

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

      祝你好运

2 个答案:

答案 0 :(得分:0)

从我在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)

答案 1 :(得分:0)

这是一个使用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]]])上工作,同时仍然接受处理原始类型的函数。