哈斯克尔:试图去除简单的国家monad得到和放弃

时间:2013-03-03 03:33:26

标签: haskell get put state-monad

为了研究State monad的细节,我试图创建一个简单的状态monad函数的完整desugared版本,完成在How does 'get' actually /get/ the initial state in Haskell?开始的思想,在J Cooper的答案中。

示例状态monad函数只是交换状态和输入值,因此(概念上)如果输入是(v,s)则输出是(s,v)。我展示了三种翻译,首先是从符号到desugared>> =和>>,然后将这些运算符置于功能位置,最后尝试替换它们并获取/放置它们的定义。

'do'版本和前两个翻译有效,但最终翻译没有。问题:

  1. 加载模块后,GHCi报告z1不在范围内。
  2. 我还没弄清楚如何表示省略在>>中传递的参数翻译。
  3. 如何解决这些问题?

    FWIW,当前的Haskell平台(GHC 7.4.2)。

    谢谢!

    -- simpleswap
    
    import Control.Monad.State
    
    -- =============================================
    -- 'Do' version
    simpleswap1 :: String -> State String String
    simpleswap1 inp = do
        z1 <- get
        put inp
        return z1
    
    -- =============================================
    -- Desugared to >>= and >>
    simpleswap2 :: String -> State String String
    simpleswap2 inp = 
        get >>= 
        \z1 -> put inp >>
        return z1 
    
    -- =============================================
    -- >>= and >> changed to function position
    simpleswap3 :: String -> State String String
    simpleswap3 inp = 
        (>>=) get 
        (\z1 -> (>>) (put inp)  (return z1) )
    
    
    -- =============================================
    -- Attempt to translate >>=, >>, get and put
    
    simpleswap4 :: String -> State String String
    simpleswap4 inp = 
        state $ \s1 -> 
            -- (>>=) 
            let (a2, s2) = runState ( {- get -}  state $ \sg -> (sg,sg) ) s1
            in  runState (rhs1 a2) s2
            where 
                rhs1 a2 = \z1 -> 
                -- (>>)
                    state $ \s3 -> 
                        let (a4, s4) = runState ( {- put inp -}  state $ \_ -> (inp, ()) ) s3
                        in runState (rhs2 a4) s4
                        where
                            rhs2 a4 = return z1
    
    -- =============================================
    main = do
        putStrLn "version 1004"
        let v = "vvv"
        let s  = "sss"
        putStrLn ("Before val: " ++ v ++ "  state: " ++ s)    
        let (v2, s2) = runState (simpleswap4 v) s
        putStrLn ("After val: " ++ v2 ++ "  state: " ++ s2)
    
    -- =============================================
    

1 个答案:

答案 0 :(得分:2)

simpleswap4中有一些小错误。这是一个更正版本:

simpleswap4 :: String -> State String String
simpleswap4 inp = 
    state $ \s1 -> 
        -- (>>=) 
        let (z1, s2) = runState ( {- get -}  state $ \sg -> (sg,sg) ) s1
        in  runState (rhs1 z1) s2
        where 
            rhs1 z1 = 
            -- (>>)
                state $ \s3 -> 
                    let (_, s4) = runState ( {- put inp -}  state $ \_ -> ((), inp) ) s3
                    in runState rhs2 s4
                    where
                        rhs2 = return z1

我已将a2重命名为z1(第5行和第6行)。这不会改变语义,但强调由desugared get调用返回的对的第一个组件实际上是在先前版本的z1中绑定到simpleswap的结果

rhs1的类型应为String -> State String String。在您的版本中,它会获得一个额外的lambda绑定变量。目前还不清楚a2z1之间的差异应该在您的版本中。删除lambda(在第8行中)还具有修复范围问题的优势。您在嵌套的z1 - 子句中使用where,但where只能看到绑定在其附加的声明左侧的变量。

在第11行,我已将a4替换为_。这是为了强调(>>)确实丢弃了第一个动作的结果。因此,rhs2也未针对此结果进行参数化。