在Scala中编写状态单子

在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) 我希望使用状态单子的

我从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 = ???
如何填写详细信息,即

  • 如何实例化层次结构以处理具体列表
  • 如何根据上述内容实现get和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