在 Haskell 的 do 块中使用 let 中的状态

Joe*_*nge 1 haskell state-monad

我有以下数据结构和功能:

data BTree a = BLeaf | BNode (BTree a) a (BTree a) deriving (Show, Eq)
freshNodesS :: BTree String -> State [String] (BTree String)
freshNodesS BLeaf = return BLeaf
freshNodesS (BNode l m r) = do  l' <- freshNodesS l
                                let m' = getFresh m s
                                let s' = m : s
                                r' <- freshNodesS r s'
                                return (BNode l' m' r')
Run Code Online (Sandbox Code Playgroud)

有一个问题,我实际上想使用freshNodesS l应该给出输出的状态(BTree String, [String]),但是在我不能使用的 do 块中(l', s) <- freshNodesS l,我看到的唯一选择是将所有内容都放在 lambda 函数中。但是有没有办法我仍然可以使用 do 符号?


在@chi 所说的之后,我做了这个:

freshNodesS BLeaf           = return BLeaf
freshNodesS (BNode l m r)   = do    l' <- freshNodesS l
                                    m' <- getFreshS m
                                    r' <- freshNodesS r
                                    return (BNode l' m' r')
                    
getFreshS :: String -> State [String] String
getFreshS x = state $ (\s -> let newx = getFresh x s in (newx, newx: s))
Run Code Online (Sandbox Code Playgroud)

那奏效了。

chi*_*chi 7

Statemonad的全部意义在于自动传递状态,而无需您明确地这样做。几乎没有函数应该s作为参数获取或返回它。

例如,

let m' = getFresh m s
Run Code Online (Sandbox Code Playgroud)

是可疑的,它可能应该阅读

m' <- getFresh m
Run Code Online (Sandbox Code Playgroud)

我们会在哪里getFresh :: String -> State [String] String

整个代码应该读为

       do l' <- freshNodesS l
          m' <- getFresh m
          r' <- freshNodesS r
          return (BNode l' m' r')
Run Code Online (Sandbox Code Playgroud)

请注意没有ss'从未被提及。这应该看起来像命令式代码,其中每个函数调用都会修改可变状态变量,即使代码没有明确提及。

现在,getFresh您将不得不与州打交道,因为没有办法解决这个问题。如果您的Statemonad 是标准的,您可以使用get和访问状态put。你可能需要类似的东西

getFresh :: String -> State [String] String
getFresh m = do
   s <- get          -- read the current state
   let m' = ...      -- compute a fresh name m'
   let s' = m' : s   -- mark m' as used
   put s'            -- write the current state
   return m'
Run Code Online (Sandbox Code Playgroud)