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

Dam*_*les 4 scala scalaz applicative reader-monad

这个问题关系到这一个,在那里我是想了解如何使用读者单子Scala中.

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

import scalaz.syntax.applicative._
val alwaysHello2: ReaderInt[String] = "hello".point[ReaderInt]
Run Code Online (Sandbox Code Playgroud)

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

Tra*_*own 11

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

scala> import scalaz.Reader, scalaz.syntax.applicative._
import scalaz.Reader
import scalaz.syntax.applicative._

scala> import scala.reflect.runtime.universe.{ reify, showCode }
import scala.reflect.runtime.universe.{reify, showCode}

scala> type ReaderInt[A] = Reader[Int, A]
defined type alias ReaderInt

scala> showCode(reify("hello".point[ReaderInt]).tree)
res0: String = `package`.applicative.ApplicativeIdV("hello").point[$read.ReaderInt](Kleisli.kleisliIdMonadReader)
Run Code Online (Sandbox Code Playgroud)

(您通常不希望scala.reflect.runtime在实际代码中使用,但对于像这样的调查来说非常方便.)

当编译器看到你试图调用.point[ReaderInt]没有point方法的类型时- 在这种情况下String- 开始查找隐式转换,它将转换String为具有匹配point方法的类型(这称为"浓缩")斯卡拉).我们可以从输出中看到,showCode它找到的隐式转换是ApplicativeIdVapplicative语法对象中调用的方法.

然后它将此转换应用于String,从而产生类型的值ApplicativeIdV[String].这种类型的point方法如下所示:

def point[F[_] : Applicative]: F[A] = Applicative[F].point(self)
Run Code Online (Sandbox Code Playgroud)

对于像这样的东西,这是语法糖:

def point[F[_]](implicit F: Applicative[F]): F[A] = F.point(self)
Run Code Online (Sandbox Code Playgroud)

所以它需要做的下一件事是找到一个Applicative实例F.在你的情况,你已经明确指定了FReaderInt.它将别名解析为Reader[Int, _],它本身就是别名Kleisli[Id.Id, Int, _],并开始寻找实例.

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