【问题标题】:Programming a state monad in Scala在 Scala 中编写状态单子
【发布时间】:2020-07-11 13:09:48
【问题描述】:

状态单子看起来像我从 Philip Wadler 的函数式编程单子中借用的理论:

type M a = State → (a, State) 
type State = Int
unit :: a → M a
unit a = λx. (a, x)
(*) :: M a → (a → M b) → M b
m * k = λx. 
         let (a, y) = m x in 
         let (b, z) = k a y in 
         (b, z)

我想使用状态单子的方式如下:

给定一个列表 L,我希望我的代码的不同部分获取此列表并通过在其末尾添加新元素来更新此列表。

我猜上面会修改为:

type M = State → (List[Data], State) 
type State = List[Data]
def unit(a: List[Data]) = (x: State) => (a,x)
def star(m: M, k: List[Data] => M): M = {
 (x: M) => 
   val (a,y) = m(x)
   val (b,z) = k(a)(y)
   (b,z)
} 
def get = ???
def update = ???

我如何填写详细信息,即?

  1. 如何实例化我的层次结构以处理具体列表?
  2. 如何实现上述的get和update?

最后,我将如何使用 Scala 的语法和 flatMap 和单元来做到这一点?

【问题讨论】:

  • 如果您将 State 设为一个类并在其中定义 flatMap,这可能会更容易实现。您可以在伴随对象中定义unit。 - 不管怎样,get 很简单,你会收到一个 M 并返回它的 Stateupdate 将是,您会收到一个 M 和一个新的 State,然后创建一个新的 M
  • @LuisMiguelMejíaSuárez 感谢您的评论。 “接收M”到底是什么意思。如果它的 M 是上面的 M 类型,我不知道它的状态是什么,因为它会是一个函数。你的意思是一对 x 状态?
  • 这个链接很好地解释了状态单子在haskell mmhaskell.com/monads/state的实践中的使用@

标签: scala monads


【解决方案1】:

您的M 定义不正确。它应该以a/A 作为参数,如下所示:

type M[A] = State => (A, State)

您在其他地方也错过了该类型参数。

unit 应该有这样的签名:

def unit[A](a: A): M[A]

star 应该有这样的签名:

def star[A, B](m: M[A], k: A => M[B]): M[B]

希望这能让功能更加清晰。

unit 的实现几乎相同:

def unit[A](a: A): M[A] = x => (a, x)

但是,在star 中,您的 lambda 参数 (x) 的类型是 State,而不是 M,因为 M[B] 基本上是 State => (A, State)。其余的你做对了:

def star[A, B](m: M[A])(k: A => M[B]): M[B] = 
  (x: State) => {
    val (a, y) = m(x)
    val (b, z) = k(a)(y)
    (b, z)
  }

编辑:根据@Luis Miguel Mejia Suarez 的说法:

如果您将 State 设为一个类并在其中定义 flatMap,它可能会更容易实现。您可以在伴随对象中定义单位。

他建议final class State[S, A](val run: S => (A, S)),这也可以让你使用像>>=这样的中缀函数。

另一种方法是将State 定义为函数S => (A, S) 的类型别名,并使用隐式类对其进行扩展。

type State[S, A] = S => (A, S)
object State {
  //This is basically "return"
  def unit[S, A](a: A): State[S, A] = s => (a, s)
}

implicit class StateOps[S, A](private runState: S => (A, S)) {
  //You can rename this to ">>=" or "flatMap"
  def *[B](k: A => State[S, B]): State[S, B] = s => {
    val (a, s2) = runState(s)
    k(a)(s2)
  }
}

如果你对get的定义是

将结果值设置为状态并保持状态不变 (借用Haskell Wiki),那么你可以这样实现:

def get[S]: State[S, S] = s => (s, s)

如果您的意思是要提取状态(在本例中为List[Data]),则可以使用execState(在StateOps 中定义):

def execState(s: S): S = runState(s)._2

这是一个糟糕的示例,说明如何将元素添加到 List

def addToList(n: Int)(list: List[Int]): ((), List[Int]) = ((), n :: list)

def fillList(n: Int): State[List[Int], ()] =
  n match {
    case 0 => s => ((), s)
    case n => fillList(n - 1) * (_ => addToList(n))
  }

println(fillList(10)(List.empty)) 给了我们这个(第二个元素可以用execState 提取):

((),List(10, 9, 8, 7, 6, 5, 4, 3, 2, 1))

【讨论】:

  • @Rodrigo 你可以用*链接他们,比如>>=
  • 谢谢!稍后让我看看
  • 我的意思是final class State[S, A](val run: S => A)
  • @LuisMiguelMejíaSuárez 抱歉,我会更改我的答案以明确您的意思。起初我实际上有case class State[S, A](runState: S => (A, S)),但后来我想我只是使用带有扩展方法的类型别名,尽管我认为使用类更合适,因为Haskell使用newtype而不是type
猜你喜欢
  • 1970-01-01
  • 2017-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多