I'm learning Haskell through writing toy-language. And I wondering how combine let with nested-do constraction. I want to write something like that (completely usual use-case but with State-monad in do-notation):
let x' = if special x then do ... else x
more precisely I want to write someting like this code:
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
I guess I could write something like that:
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
but:
- I really wonder how write some "unbalanced" nested do.
- moveDefToVirt have the same problem (less sharp due to the smaller size of function): we need state monad only in "then" branch and not in else
The two branches of an
ifmust have the same type. If you need one with a monadic type and the other with a "pure" type, you need to make those types equal using areturn(or, equivalently,pure).Here's an example using the IO monad. You can adapt it to your case.
Main points:
ifcontains an action that you want to run right now, you must avoidlet res = if ...and instead useres <- if .... The former definesresto be the action itself, the latter executes the action and definesresto be the result produced by the action.res <- if ...must have the same monadic type. Usereturnto make it so.There is no way to use the standard
ifwith a monadic then branch and a non-monadic else branch. At best, we can define a custom "if" function that does that, e.g.This can be used as
You can have a similar helper function for the case where the then branch is "pure" and the else branch is monadic.