斯卡拉(Scala)和State Monad

标签 scala state-monad

我一直在试图了解莫纳德州。虽然使用起来并不总是那么容易,但是它的用法并不多。但是,我发现有关Monad州的每一次讨论都具有基本相同的信息,而且总会有一些我不理解的地方。

this帖子为例。作者具有以下内容:

case class State[S, A](run: S => (A, S)) {
...
  def flatMap[B](f: A => State[S, B]): State[S, B] =
    State(s => {
      val (a, t) = run(s)
      f(a) run t
    })
...
}

我可以看到类型正确排列。但是,我根本不理解第二个run

也许我看错了这个monad的全部目的。从HaskellWiki获得的印象是State monad有点像带有run允许进行转换的状态机(尽管在这种情况下,状态机实际上不像大多数状态机那样具有固定的状态转换)。如果是这种情况,那么在上面的代码中(a, t)将代表一个过渡。 f的应用将表示对该值和State的修改(生成新的State对象)。这使我完全对第二个run的含义感到困惑。这似乎是第二次“过渡”。但这对我来说没有任何意义。

我可以看到,在生成的run对象上调用State会生成一个新的(A, S)对,当然,这是排列类型所必需的。但是我真的不知道这应该在做什么。

那么,这里到底发生了什么?在此建模的概念是什么?

编辑:2015年12月22日

因此,看来我不太清楚表达我的问题。我来试试看

在同一博客文章中,我们看到map的以下代码:
def map[B](f: A => B): State[S, B] =
  State(s => {
    val (a, t) = run(s)
    (f(a), t)
  })

显然,这里只有一个对run的调用。

我一直试图调和的模型是对run的调用通过单个状态更改来移动我们保持的状态。 map中似乎是这种情况。但是,在flatMap中,我们有两个对run的调用。如果我的模型是正确的,那将导致“跳过”状态更改。

为了利用下面提供的示例@Filppo,第一次调用run将导致返回(1, List(2,3,4,5)),第二次调用将导致(2, List(3,4,5)),实际上跳过了第一个。由于在他的示例中,紧随其后的是对map的调用,因此会生成(Map(a->2, b->3), List(4,5))

显然,这不是正在发生的事情。所以我的整个模型是不正确的。对此进行推理的正确方法是什么?

第2编辑:2015年12月22日

我只是尝试做我在REPL中所说的话。我的直觉是正确的,这使我更加困惑。
scala> val v = State(head[Int]).flatMap { a => State(head[Int]) }
v: State[List[Int],Int] = State(<function1>

scala> v.run(List(1,2,3,4,5))
res2: (Int, List[Int]) = (2,List(3, 4, 5))

因此,flatMap 的这种实现确实使跳过了一个状态。但是,当我运行@Filippo的示例时,我得到的答案与他相同。这里到底发生了什么?

最佳答案

要了解“第二次运行”,让我们对其进行“向后分析”。

签名def flatMap[B](f: A => State[S, B]): State[S, B]建议我们需要运行一个函数f并返回其结果。

要执行f函数,我们需要给它一个A。我们在哪里得到一个?
好吧,我们有run可以从A中提供S,所以我们需要一个S

因此,我们这样做:s => val (a, t) = run(s) ...
我们将其读为“给定S,执行run函数,该函数会生成A和新的S。这是我们的”首次”运行。

现在我们有了一个A,我们可以执行f。这就是我们想要的,f(a)为我们提供了一个新的State[S, B]
如果我们这样做,那么我们就有一个函数,它接受S并返回Stats[S, B]:

(s: S) => 
   val (a, t) = run(s)
   f(a) //State[S, B]

但是函数S => State[S, B]不是我们想要返回的!我们只想返回State[S, B]

我们该怎么做?我们可以将此函数包装为State:
State(s => ... f(a))

但这是行不通的,因为State接受S => (B, S)而不是S => State[B, S]
因此,我们需要从(B, S)中获取State[B, S]
我们只需调用其run方法并为其提供上一步所产生的状态即可!
这是我们的“第二次”运行。

因此,我们通过flatMap执行了以下转换:
s =>                   // when a state is provided
  val (a, t) = run(s)  // produce an `A` and a new state value
  val resState = f(a)  // produce a new `State[S, B]`
  resState.run(t)      // return `(S, B)`

这给了我们S => (S, B),我们只是用State构造函数包装了它。

查看这些“两次运行”的另一种方法是:
首先-我们使用“我们的” run函数自行转换状态
第二个-我们将转换后的状态传递给函数f,然后让它执行自己的转换。

因此,我们需要一种“链式”状态转换。这正是monad所做的:它们使我们能够按顺序调度计算。

关于斯卡拉(Scala)和State Monad,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34407212/

相关文章:

scala - 为什么 Scala 集合没有任何人类可读的方法,如 .append、.push 等

scala - HList/KList 是否适合作为方法参数?如何引用?类型列表?

haskell - 卡在状态单子(monad)中

haskell - 状态单子(monad) : Transitioning from one state type to another

Scala Random创建随机对象时种子会做什么

java - 为什么 LocalDate 没有实现 Comparable<LocalDate>?

scala - 如何更改功能测试的 Guice 绑定(bind)?

haskell - 在 State Monad 中获取 Gamestate 值

haskell - 创建我自己的状态 monad 转换器模块,隐藏底层状态 monad

haskell - 在 Haskell 中结合 ST 和 List 单子(monad)