Scalaz:`scalaz.syntax.applicative._`是如何发挥作用的

时间:2016-07-27 19:56:01

标签: scala scalaz applicative reader-monad

这个问题与trust proxy有关,我试图了解如何在Scala中使用阅读器monad。

在答案中,autor使用以下代码获取ReaderInt[String]的实例:

import scalaz.syntax.applicative._
val alwaysHello2: ReaderInt[String] = "hello".point[ReaderInt]

Scala使用哪种机制来解析表达式"hello".point[ReaderInt]的类型,以便它使用正确的point函数?

2 个答案:

答案 0 :(得分:11)

每当你试图弄清楚这样的事情时,这是一个很好的第一步,就是使用反射API去解释表达式:

scala.reflect.runtime

(您通常不希望在实际代码中使用.point[ReaderInt],但这对于此类调查非常方便。)

当编译器看到您尝试在没有point方法的类型上调用String时 - 在这种情况下String - 它开始寻找可以转换的隐式转换point成为具有匹配showCode方法的类型(在Scala中称为“浓缩”)。我们可以从ApplicativeIdV的输出中看到,它找到的隐式转换是applicative语法对象中名为String的方法。

然后,它会将此转化应用于ApplicativeIdV[String],从而产生类型为point的值。此类型的def point[F[_] : Applicative]: F[A] = Applicative[F].point(self) 方法如下所示:

def point[F[_]](implicit F: Applicative[F]): F[A] = F.point(self)

这是这样的语法糖:

Applicative

所以它需要做的下一件事是为F找到F个实例。在您的情况下,您明确指出ReaderIntReader[Int, _]。它将别名解析为Kleisli[Id.Id, Int, _],它本身就是Kleisli的别名,并开始查找实例。

它看起来的第一个地方之一是Kleisli随播广告对象,因为它想要一个包含showCode的类型的隐式值,实际上Kleisli.kleisliIdMonadReader告诉我们它找到的是ReaderInt[String]。那时已经完成了,我们得到了我们想要的/task/7

答案 1 :(得分:4)

我想更新以前的答案,但既然你创建了单独的问题,我就把它放在这里。

scalaz.syntax

让我们考虑point示例,您可以对其他方法应用相同的推理。

point(或haskell的return)或pure(只是类型别名)属于Applicative特质。如果您想在某些F内添加内容,则此Applicative至少需要F个实例。

通常,您将使用导入隐式提供它,但您也可以明确指定它。

在第一个问题的示例中,我将其分配给val

implicit val KA = scalaz.Kleisli.kleisliIdApplicative[Int]

因为scala无法找出此应用程序的相应Int类型。换句话说,它不知道适用于引入哪个Reader 。(虽然有时编译器可以解决它)

对于具有一个类型参数的Applicatives,我们可以通过使用import

来引入隐式实例
import scalaz.std.option.optionInstance 
import scalaz.std.list.listInstance
等等......

好的,你有实例。现在您需要在其上调用point。 你有几个选择:

<强> 1。直接访问方法:

scalaz.std.option.optionInstance.point("hello")
KA.pure("hello")

<强> 2。明确地将其从隐式上下文中拉出来:

Applicative[Option].point("hello") 

如果您查看Applicative对象,您会看到

object Applicative {
  @inline def apply[F[_]](implicit F: Applicative[F]): Applicative[F] = F
}

apply的实现仅返回某些类型Applicative[F]的相应F实例。

所以Applicative[Option].point("hello")转换为。{     Applicative[Option].apply(scalaz.std.option.optionInstance) 最后只是optionInstance

第3。使用语法

import scalaz.syntax.applicative._ 

将此方法引入隐式范围:

  implicit def ApplicativeIdV[A](v: => A) = new ApplicativeIdV[A] {
    val nv = Need(v)
    def self = nv.value
  }

  trait ApplicativeIdV[A] extends Ops[A] {
    def point[F[_] : Applicative]: F[A] = Applicative[F].point(self)
    def pure[F[_] : Applicative]: F[A] = Applicative[F].point(self)
    def η[F[_] : Applicative]: F[A] = Applicative[F].point(self)
  }  ////

那么,每当您尝试在point

上调用String
"hello".point[Option] 

编译器意识到,String没有方法point并开始查看隐含,它是如何从point获得String的内容。< / p>

它发现,它可以将String转换为ApplicativeIdV[String],它确实有方法point

 def point[F[_] : Applicative]: F[A] = Applicative[F].point(self)

所以最后 - 你的电话去了

new ApplicativeIdV[Option]("hello")


或多或少scalaz中的所有类型类都以相同的方式工作。 对于sequence,实现是

def sequence[G[_]: Applicative, A](fga: F[G[A]]): G[F[A]] =
    traverse(fga)(ga => ga)

G之后的冒号意味着Applicative[G]应该隐式提供。 它基本上与:

相同
def sequence[G[_], A](fga: F[G[A]])(implicit ev: Applicative[G[_]]): G[F[A]] =
        traverse(fga)(ga => ga)

所以你需要的只是Applicative [G]和Traverse [F]。

import scalaz.std.list.listInstance
import scalaz.std.option.optionInstance
Traverse[List].sequence[Option, String](Option("hello"))