这个问题与trust proxy
有关,我试图了解如何在Scala中使用阅读器monad。
在答案中,autor使用以下代码获取ReaderInt[String]
的实例:
import scalaz.syntax.applicative._
val alwaysHello2: ReaderInt[String] = "hello".point[ReaderInt]
Scala使用哪种机制来解析表达式"hello".point[ReaderInt]
的类型,以便它使用正确的point
函数?
答案 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
个实例。在您的情况下,您明确指出ReaderInt
是Reader[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"))