Haskell - Продолжаем дальше делать после проверки с помощью либо - PullRequest
0 голосов
/ 02 ноября 2018

Я новичок в 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 здесь, когда пользователь выбирает право, но тогда он не скажет строку «Вы продолжаете с опцией ...».

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, у меня больше нет доступа к выбору области действия.

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 голосов
/ 02 ноября 2018

Похоже, вам просто нужно привязать значение, возвращаемое в монаде из start, например:

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 нотации", чтобы узнать, как это переводится на лямбды и методы класса Monad (>>= и return).


Пара других предложений:

Вы можете использовать (и должны предпочитать) сопоставление с образцом вместо охранников и == (метод класса Eq, который реализуют не все типы). Э.Г.

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

Помните, что в haskell синтаксис для вызова foo для a и b равен foo a b, а не foo(a,b)

Может быть очень полезно включить предупреждения (как во время обучения, так и на основе производственного кода), например здесь я загружаю ваш файл в ghci после включения -Wall:

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, чтобы сделать его явным и отключить ошибку.

...