在Scala中编写状态单子
我从Philip Wadler的函数编程单子中借用了状态单子的理论:在Scala中编写状态单子,scala,monads,Scala,Monads,我从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) 我希望使用状态单子的
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 = ???
如何填写详细信息,即
最后,如何使用Scala的flatMap和unit语法来实现这一点?您的
M
定义不正确。它应该将a
/a
作为参数,如下所示:
type M[A] = State => (A, State)
您在其他地方也遗漏了该类型参数
单元
应具有如下签名:
def unit[A](a: A): M[A]
def star[A, B](m: M[A], k: A => M[B]): M[B]
star
应具有如下签名:
def unit[A](a: A): M[A]
def star[A, B](m: M[A], k: A => M[B]): M[B]
希望这能使函数更加清晰
您对单元的实现
基本相同:
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:
如果将状态设置为类并在其中定义flatMap,那么实现起来可能会更容易。您可以在伴生对象中定义单位 他建议
最终类状态[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
的定义为
将结果值设置为状态并保持状态不变
(借用自),然后您可以这样实现它:
def unit[A](a: A): M[A]
def star[A, B](m: M[A], k: A => M[B]): M[B]
如果您的意思是要提取状态(在本例中为列表[数据]
),则可以使用execState
(在StateOps
中定义它):
下面是一个可怕的示例,说明了如何将元素添加到
列表中
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
提取):
如果您将状态设置为类并在其中定义flatMap
,那么实现起来可能会更容易。您可以在伴生对象中定义单位
。-无论如何,get
非常简单,您收到一个M并返回其状态。更新将是,你收到一个M和一个新的状态,你创建了一个新的M。@LuisMiguelMejíaSuárez感谢你的评论。你说的“收到M”到底是什么意思。如果它是上面的M类型,我不知道它的状态是什么,因为它是一个函数。你是说一对x状态吗?这个链接很好地解释了haskell@Rodrigo中状态单子的实际用法。你可以用*
链接它们,比如>=
谢谢!让我稍后再看一看,我实际上指的是类似于最终类状态[S,a](val run:S=>a)
@LuisMiguelMejíaSuárez抱歉,我将更改我的答案,以明确您的意思。实际上,我一开始有case类State[S,A](runState:S=>(A,S))
,但后来我认为我应该在扩展方法中使用类型别名,尽管我认为使用类更合适,因为Haskell使用newtype
而不是type