4

为了研究 State monad 的细节,我正在尝试为自己创建一个简单的 state monad 函数的完整脱糖版本,完成在“如何获取”实际上 /get/ Haskell 中的初始状态?,在 J Cooper 的回答中。

示例状态单子函数只是交换状态和输入值,因此(从概念上讲)如果输入是 (v, s),那么输出是 (s, v)。我展示了三种翻译,首先从 do 符号到脱糖的 >>= 和 >>,然后将这些运算符放在函数位置,最后尝试替换它们并使用它们的定义获取/放置。

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

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

这些应该如何解决?

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)

-- =============================================
4

1 回答 1

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

我已重命名a2z1(在第 5 行和第 6 行)。这不会改变语义,但强调了脱糖get调用返回的对的第一个组件实际上是z1在之前版本的simpleswap.

的类型rhs1应该是String -> State String String。在您的版本中,它会获得一个额外的 lambda 绑定变量。a2目前尚不清楚您的版本中和z1应该有什么区别。删除 lambda(第 8 行)还具有解决范围问题的优势。您z1在嵌套where- 子句中使用,但where只能看到绑定在它所附加的声明左侧的变量。

在第 11 行,我已替换a4_. 这是为了强调(>>)确实丢弃了第一个动作的结果。因此,rhs2也没有在这个结果上参数化。

于 2013-03-03T10:14:13.940 回答