Haskell-经过任一验证后继续下一步

时间:2018-11-02 03:00:51

标签: validation haskell scope 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放在此处,但是它不会说字符串“您继续选择...”。

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..."

如果我尝试将continue作为正确的选项,则会引发范围错误。同样,如果我使用return并尝试为userChoice创建原始的克隆函数,那么我将无法再访问作用域选择。

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)

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

1 个答案:

答案 0 :(得分:2)

听起来您只需要绑定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

您可以阅读有关“将符号do删除”的信息,以了解其如何转换为lambda和Monad类的方法(>>=return)。


其他一些建议:

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

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

请记住,在haskell中,在fooa上调用b的语法是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返回IO String,但是您对String的值没有做任何事情,这确实是一个错误。如果您确实要舍弃该值,则可以使用void将该值明确显示并消除错误。