如何在不执行它们的情况下绑定两个IO()monad?

abh*_*hek 2 monads haskell functional-programming

在下面的代码中,我>>用于将IO操作连接在一起.但是,AFAIU m1>>m2会被取消m1>>=(\_.m2),因此它在绑定时正在执行第一个IO操作.我希望所有打印都发生在main中,即print语句不应与输入语句交错("输入代码").既然do不允许我返回任何其他monad而不是IO [IO ()].如何获得所需的打印效果?

f :: [Int] -> IO ()
f inventory = do
        putStrLn "Enter Code\n"
        x <- getLine
        let idx = nameToIndex x 
        putStrLn "Quantity\n"
        y <- getLine
        putStrLn "More?\n"
        c <- getChar
        let q = (read y :: Int)
        let curM = if inventory !! idx >= q then (putStrLn "sdaf\n") else (putStrLn "Overflow!\n")  
        if c == 'Y' then curM>>(f (update inventory idx)) else curM

main = f [1, 2]
Run Code Online (Sandbox Code Playgroud)

Dan*_*ner 5

我不是100%肯定我理解这个问题,但我认为它是这样的:你想与用户进行一些互动,存储有关交互的信息,然后在结束时立即显示所有信息.整个互动.

这是一个非常简化的代码版本,它会跳过所有业务逻辑,并不断询问用户是否要继续.

prompt = do
    putStrLn "Continue?"
    s <- getLine
    case s of
        "y" -> putStrLn "Okay, let's continue." >> prompt
        _ -> return ()

main = prompt
Run Code Online (Sandbox Code Playgroud)

认为你要求的效果是延迟显示"好的,让我们继续." 直到用户停止点击"y".那没问题.有很多方法可以做到这一点.最灵活的是prompt在完成后返回它想要执行的动作:

prompt = do
    putStrLn "Continue?"
    s <- getLine
    case s of
        "y" -> do
            act <- prompt
            return (putStrLn "Okay, let's continue." >> act)
        _ -> return (return ())

main = do
    act <- prompt
    act
Run Code Online (Sandbox Code Playgroud)

(有一些组合器可以使这个代码更紧凑.)但我不喜欢这种设计; 它很难反省结果prompt.更专业但也更易于维护的方法是返回一些描述交互的数据,然后调用者可以将其转化为IO总结事物的动作.在这种情况下,字符串列表似乎是一个合适的描述.

prompt = do
    putStrLn "Continue?"
    s <- getLine
    case s of
        "y" -> do
            results <- prompt
            return ("Okay, let's continue." : results)
        _ -> return []

main = do
    results <- prompt
    mapM_ putStrLn results
Run Code Online (Sandbox Code Playgroud)

希望这个解释很清楚,你可以将这个想法与更复杂的业务逻辑结合起来.