Haskell:非对称嵌套 do 表示法和 let

Kon*_*sev 0 haskell do-notation

我正在通过编写玩具语言来学习 Haskell。我想知道如何将 let 与嵌套 do 构造结合起来。我想写类似的东西(完全常见的用例,但在 do 表示法中使用 State-monad):

let x' = if special x then do ... else x
Run Code Online (Sandbox Code Playgroud)

更准确地说,我想写一些像这样的代码:

moveOpersToVirt :: [IOper] -> State (RegNum, M.Map OperNum Var, TRegs) [IOper]
moveOpersToVirt ((i, Oper op r arg1 arg2):os) = do
  (virt_rn, mp, regs) <- get
  let arg2' = if isRegGlobal arg2
      -- get previously remembered value for "global reg arg2"
      then getRegStrict regs (getValRegStrict arg2) 
      else arg2
  let arg1' = ...
  let r' = if isRegGlobal r
    -- substitute "global reg r" to "virtual reg r1"
    -- and remember it in State
    then do 
      let virt_rn1 = virt_rn + 1
      let r1 = Var virt_rn1
      let regs1 = setReg regs r r1
      let mp1 = M.insert i r1 mp
      put (virt_rn1 + 1, mp1, regs1)
      r1
    else r
  rest_opers  <- moveOpersToVirt os
  return $ (Oper op r' arg1' arg2'):rest_opers 
Run Code Online (Sandbox Code Playgroud)

我想我可以写这样的东西:

moveOpersToVirt :: [IOper] -> State (RegNum, M.Map OperNum Var, TRegs) [IOper]
moveOpersToVirt ((i, Oper op r arg1 arg2):os) = do
  (virt_rn, mp, regs) <- get
  let arg2' = useToVirt regs arg2   -- pure function
  let arg1' = useToVirt regs arg2   -- pure function
  r' <- moveDefToVirt r             -- function in the same State Monad
  rest_opers <- moveOpersToVirt os
  return $ (Oper op r' arg1' arg2'):rest_opers
Run Code Online (Sandbox Code Playgroud)

但:

  • 我真的想知道如何编写一些“不平衡”的嵌套。
  • moveDefToVirt 也有同样的问题(由于函数尺寸较小而不太尖锐):我们仅在“then”分支中需要状态 monad,而在 else 中不需要

chi*_*chi 6

an 的两个分支if必须具有相同的类型。如果您需要一个具有一元类型,另一个具有“纯”类型,则需要使用 a return(或等效的pure)使这些类型相等。

这是使用 IO monad 的示例。您可以根据您的情况进行调整。

main :: IO ()
main = do
   putStrLn "enter True of False"
   x <- readLn   
   res <- if x
      then do
         putStrLn "You entered True: write somehting now"
         getLine             -- its result is bound to res
      else return "nothing"  -- res is bound to "nothing"
   putStrLn $ "final result: " ++ res
Run Code Online (Sandbox Code Playgroud)

要点:

  • 如果您的分支if包含您想要立即运行的操作,则必须避免let res = if ...并使用res <- if .... 前者定义res为动作本身,后者执行动作并定义res为动作产生的结果。
  • 的两个分支res <- if ...必须具有相同的单子类型。用return它来做到这一点。

无法将标准if与单子 then 分支和非单子 else 分支一起使用。充其量,我们可以定义一个自定义的“if”函数来执行此操作,例如

ifMon :: Monad m => Bool -> m a -> a -> m a
ifMon True x  _ = x
ifMon False _ y = return y
Run Code Online (Sandbox Code Playgroud)

这可以用作

do
   putStrLn "blah"
   res <- ifMon condition
          (do
              putStrLn "then branch"
              return "abc")
          "else result, non monadic"
Run Code Online (Sandbox Code Playgroud)

对于 then 分支是“纯”分支而 else 分支是单子的情况,您可以使用类似的辅助函数。