每当您尝试解决此类问题时,最好的第一步是使用反射 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)
(你通常不想使用scala.reflect.runtime
在真实代码中,但对于这样的调查来说非常方便。)
当编译器发现你试图调用.point[ReaderInt]
在没有 a 的类型上point
方法——在本例中String
- 它开始寻找隐式转换来转换String
转换为具有匹配的类型point
方法(这在 Scala 中称为“丰富”)。从输出中我们可以看到showCode
它找到的隐式转换是一个名为的方法ApplicativeIdV
in the 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
is ReaderInt
。它将别名解析为Reader[Int, _]
,它本身就是一个别名Kleisli[Id.Id, Int, _]
,并开始寻找实例。
它首先看到的地方之一将是Kleisli
伴生对象,因为它需要一个包含以下类型的隐式值Kleisli
,事实上showCode
告诉我们它找到的是Kleisli.kleisliIdMonadReader
。到那时就完成了,我们得到了ReaderInt[String]
我们要。