操纵Haskell Monad状态

时间:2019-03-02 05:11:51

标签: haskell monads

有点类似于this question,我正在尝试找出 如何绕过Haskell Monad状态。 团队中的每个Employee都将替换为相应的Employee' 同时保持一些简单的状态。这是代码:

module Main( main ) where
import Control.Monad.State

data Employee  = EmployeeSW  Int Int | EmployeeHW Int String deriving ( Show )
data Employee' = EmployeeSW'     Int | EmployeeHW'    String deriving ( Show )

scanTeam :: [Employee] -> State (Int,Int) [Employee']
scanTeam [    ] = return []
scanTeam (p:ps) = scanEmployee p -- : scanTeam ps ???

scanEmployee :: Employee -> State (Int,Int) Employee'
scanEmployee (EmployeeSW id s) = do
    (num,raise) <- get
    put (num+1,raise)
    return (EmployeeSW' (s+raise))
scanEmployee (EmployeeHW id s) = do
    (num,raise) <- get
    put (num+1,raise)
    return (EmployeeHW' (s++(show raise)))

startState = (0,3000)

t = [(EmployeeHW 77 "Hundred"),(EmployeeSW 66 500),(EmployeeSW 32 200)]

main = print $ evalState (scanTeam t) startState

我最终希望将scanEmployee pscanTeam ps连接起来, 所以我尝试提取scanEmployee p的片段并以某种方式粘合 它们与scanTeam ps一起使用。到目前为止,我失败了。 实际上,我什至不确定状态是否可以在它们之间移动(?)。

1 个答案:

答案 0 :(得分:4)

由于State是monad,因此可以使用do表示法来定义State计算。 (State的一个实例Monad贯穿状态,因此do块中一个语句的结束状态成为下一个语句的起始状态。)

因此,在一个do块中,我要执行以下操作:

  1. 处理列表中的第一个Employee以获取新的Employee
  2. 递归处理列表的其余部分
  3. 将两个结果放回去,并用作State ful计算的返回值。
scanTeam :: [Employee] -> State (Int,Int) [Employee']
scanTeam [    ] = return []
scanTeam (p:ps) = do
    newP <- scanEmployee p
    newPs <- scanTeam ps
    return (newP:newPs)

事实证明,“ map在单子语境中”通常非常有用,因此,如果您已经准备好了,它在标准前奏中会以mapM :: Monad m => (a -> m b) -> [a] -> m [b](也称为traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b))的形式出现顺着兔子的洞下去。

scanTeam = mapM scanEmployee