1

我是一个斯卡拉新手。我来自 Java 的背景。我一直在阅读 monads 并且已经形成了关于它的一般概念。虽然我可以理解类型的mapandflatMap操作,例如当涉及到sList时,我无法理解它们的含义。reader monad有人可以举一些简单的例子吗?

我知道我们需要 ReaderMonads 来促进一元函数组合,以便我们可以使用像 for - 理解这样的花哨的语法。我也明白,我们需要满足单子神才能实现这一点。我只想了解“地图和平面地图”对功能意味着什么?

4

1 回答 1

4

reader monad,经常被写Reader[A, B],只是函数类型A => B。Scala 中的 monad 编码如下所示:

trait Monad[M[_]] {
  def pure[A](a: A): M[A]
  def flatMap[A, B](ma: M[A])(f: A => M[B]): M[B]
}

wheremap可以这样pure实现flatMap

def map[A, B](ma: M[A])(f: A => B): M[B] = flatMap(ma)(a => pure(f(a)))

所以我们需要做的第一件事是让我们的二元类型构造函数Reader适合Monad预期的一元类型构造函数。这是通过固定第一个(输入)类型参数并保留第二个(输出)类型参数来完成的。

implicit def readerMonad[X]: Monad[X => ?] = ???

?这里的使用是通过美妙的kind-projector编译器插件)。

从 开始pure,我们将出现的替换M[_]X => _

def pure[A](a: A): X => A

给定某个类型的值A,我们必须返回一个函数,给定一个类型的值X,返回一个类型的值A。唯一可能是常量函数。

def pure[A](a: A): X => A = _ => a

现在换flatMap..

def flatMap[A, B](ma: X => A)(f: A => (X => B)): X => B = (x: X) => ???

这有点棘手,但我们可以使用类型来指导我们实现!我们有:

ma: X => A
f: A => (X => B)
x: X

我们想要一个B. 我们知道如何做到这一点的唯一方法是 via f,它需要 anA和 an X。我们正好有一个Xfrom x,但我们需要一个A. 我们看到我们只能得到一个Afrom ma,它想要一个X,它又只x提供了我们。因此我们有..

def flatMap[A, B](ma: X => A)(f: A => (X => B)): X => B =
  (x: X) => {
    val a = ma(x)
    val b = f(a)(x)
    b
  }

大声朗读,flatMapon表示从“环境”(或“配置”)Reader中读取一些值。然后,分支,从环境中读取另一个值。AXAB

我们也可以继续map手动实现:

def map[A, B](ma: X => A)(f: A => B): X => B = ???

查看参数X => AA => B,以及预期的输出X => B,这看起来与函数组合完全一样,确实如此。

使用进口的示例用法:

import cats.data.Reader

假设我们有某种Config类型:

case class Config(inDev: Boolean, devValue: Int, liveValue: Int)

它告诉我们是否处于“开发”环境中,并为我们提供“开发”和“生活”的价值。我们可以从编写一个简单的为我们Reader[Config, Boolean]读出inDev标志开始。

val inDev: Reader[Config, Boolean] = Reader((c: Config) => c.inDev)

我们可以编写一个简单的函数,给定一些布尔值,它将读取适当的值。

def branch(flag: Boolean): Reader[Config, Int] =
  if (flag) Reader((c: Config) => c.devValue)
  else      Reader((c: Config) => c.liveValue)

现在我们可以组合两者:

val getValue: Reader[Config, Int] =
  for {
    flag <- inDev
    value <- branch(flag)
  } yield value

现在我们可以Int通过传入各种值来获取我们的Config值:

getValue.run(Config(true, 1, 2)) // 1
getValue.run(Config(false, 1, 2)) // 2

这通常可以用作实现依赖注入的好方法,只使用函数!

于 2016-03-28T06:35:33.033 回答