Haskell - 验证后继续下一步

Haskell - Continuing to next do after validating with either

我是 Haskell 的新手,我使用 do 表示法通过 Either 来验证用户的选择。

userChoice :: String -> Either String String
userChoice option
    | option == "1" = Right "You proceed with option 1."
    | option == "2" = Right "You proceed with option 2"
    | option == "3" = Right "You proceed with option 3"
    | option == "4" = Right "You proceed with option 4"
    | otherwise = Left $ "\n\nInvalid Option...\nWhat would you like to do?\n" ++ displayOptions(0)

displayOptions :: Int -> String
displayOptions option
    | option == 0 = "1 - Option 1\n2 - Option 2\n3 - Option 3\n4 - Option 4"
    | otherwise = "invalid"

main = do
    putStrLn "Welcome."
    let start startMessage = do
        putStrLn startMessage
        let ask message = do
            putStrLn message
            choice <- getLine
            either ask ask $ userChoice(choice)
        ask $ "What would you like to do?\n" ++ displayOptions(0)
    start "Choose a number between 1-4."

这工作正常,但在用户选择了正确的选项后,我想继续执行程序的下一部分。我可以使用 return 但我丢失了用户选择的选项。

举个例子,我可以在用户选择权限的时候把return放在这里,但是它不会说字符串"You proceed with option..."。

main = do
    putStrLn "Welcome."
    let start startMessage = do
        putStrLn startMessage
        let ask message = do
            putStrLn message
            choice <- getLine
            either ask return $ userChoice(choice)
        ask $ "What would you like to do?\n" ++ displayOptions(0)
    start "Choose a number between 1-4."

    -- putStrLn userChoice2(choice)

    let continue continueMessage = do
        putStrLn continueMessage
        let ask message = do
            putStrLn message
            choice <- getLine
            either continue continue $ userChoice(choice)
        ask $ "What would you like to do?"
    continue "We are now here..."

如果我尝试将继续作为正确的选项,那么它会抛出范围错误。同样,如果我使用 return 并尝试为 userChoice 创建一个原始的克隆函数,我将无法再访问范围 choice is in.

userChoice2 :: String -> String
userChoice2 option
    | option == "1" = "You proceed with option 1."
    | option == "2" = "You proceed with option 2"
    | option == "3" = "You proceed with option 3"
    | option == "4" = "You proceed with option 4"
    | otherwise = "\n\nInvalid Option...\nWhat would you like to do?\n" ++ displayOptions(0)

有没有办法优雅地将它们链接在一起?

听起来您只需要绑定来自 start 的 monad 中返回的值,如下所示:

main = do
    putStrLn "Welcome."
    let start startMessage = do
        putStrLn startMessage
        let ask message = do
            putStrLn message
            choice <- getLine
            either ask return $ userChoice(choice)
        ask $ "What would you like to do?\n" ++ displayOptions(0)

    -- THE NEW BIT I ADDED:
    opt <- start "Choose a number between 1-4."
    putStrLn opt   -- THIS PRINTS "You proceed with ..."

    -- SNIP

您可以阅读 "desugaring do notation" 以了解它如何转换为 lambda 以及 Monad class(>>=return)的方法。


其他一些建议:

您可以使用(并且应该更喜欢)模式匹配来代替守卫和 ==Eq class 的一种方法,并非所有类型都实现)。例如

userChoice2 option = case option of
    "1" -> "You proceed with option 1."
    "2" -> "You proceed with option 2"

请记住,在 haskell 中,在 ab 上调用 foo 的语法是 foo a b 而不是 foo(a,b)

打开警告(无论是在学习还是在生产代码库中)都非常有帮助,例如在这里,我在打开 -Wall:

后将你的文件加载到 ghci 中
Prelude> :set -Wall
Prelude> :l k.hs
[1 of 1] Compiling Main             ( k.hs, interpreted )

k.hs:15:1: warning: [-Wmissing-signatures]
    Top-level binding with no type signature: main :: IO b
   |
15 | main = do
   | ^^^^

k.hs:24:5: warning: [-Wunused-do-bind]
    A do-notation statement discarded a result of type ‘String’
    Suppress this warning by saying
      ‘_ <- start "Choose a number between 1-4."’
   |
24 |     start "Choose a number between 1-4."
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Ok, one module loaded.

请注意,它警告您 start returns IO String 但您没有对 String 值执行任何操作,这确实是一个错误。如果您真的想丢弃该值,您可以使用 void 使其显式化并消除错误。