地图和平面地图操作对ReaderMonad意味着什么

时间:2016-03-28 06:03:40

标签: scala reader-monad

我是斯卡拉新手。我来自Java的背景。我一直在读monad,并形成了一个大致的想法。虽然我对map等类型的flatMapList操作表示赞赏,但我无法理解reader monad的含义。 有人可以提出一些简单的例子。

我知道我们需要ReaderMonads来促进一元函数的组合,这样我们就可以使用像for - comprehensions这样的花哨语法。我也明白,为了实现这一点,我们需要monad gods满意。 我想要了解的是“地图和平面图对于函数意味着什么?”

1 个答案:

答案 0 :(得分:4)

读者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]
}

其中map可以pureflatMap实现,如下所示:

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开始,我们用[{1}}替换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,它需要fA。我们只有一个来自X的{​​{1}},但我们需要一个X。我们发现,我们只能从x获得AA需要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 } 上的flatMap说要从“环境”(或“配置”)Reader中读取一些值A。然后,在X上分支,从环境中读取另一个值A

我们可以继续手动实施B

map

查看参数def map[A, B](ma: X => A)(f: A => B): X => B = ??? X => A,以及预期的输出A => B,这看起来与函数组合完全一样,确实如此。

使用cats导入的示例用法:

X => B

假设我们有一些import cats.data.Reader 类型:

Config

告诉我们我们是否处于“开发”环境中并为“dev”和“live”提供了价值。我们可以先编写一个简单的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

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