【问题标题】:MaybeT/Maybe and IO: Failsafe reading of informationMaybeT/Maybe 和 IO:信息的故障安全读取
【发布时间】:2016-08-08 13:52:42
【问题描述】:

我正在尝试读取用户输入的信息并将其解析为Person 类型,该类型使用Gender 类型。为此,我使用以下代码:

data Person = Person String Int Gender String
data Gender = Male | Female | NotSpecified deriving Read

instance Show Gender where
    show Male = "male"
    show Female = "female"
    show NotSpecified = "not specified"

instance Show Person where
    show (Person n a g j) = "Person {name: " ++ n ++ ", age: " ++ show a ++ 
        ", gender: " ++ show g ++ ", job: " ++ j ++ "}"

readPersonMaybeT :: MaybeT IO ()
readPersonMaybeT = do
    putStrLn "Name?:"
    name <- getLine
    putStrLn "Age?:"
    ageStr <- getLine
    putStrLn "Gender?:"
    genderStr <- getLine
    putStrLn "Job?:"
    job <- getLine

    let newPerson = Person name (read ageStr) (read genderStr) job
    putStrLn $ show newPerson

现在我想让这更加安全 - 为了实现这一点,我尝试使用 MaybeT monad。使用这个,我得到了这个代码:

readPersonMaybeT :: MaybeT IO ()
readPersonMaybeT = do
    lift $ putStrLn "Name?:"
    name <- lift getLine
    lift $ putStrLn "Age?:"
    ageStr <- lift getLine
    lift $ putStrLn "Gender?:"
    genderStr <- lift getLine
    lift $ putStrLn "Job?:"
    job <- lift getLine

    let newPerson = Person name (read ageStr) (read genderStr) job
    lift $ putStrLn "show newPerson"

它由 GHCI 编译/加载,但是当我尝试执行 readPersonMaybeT 函数时,我收到错误消息

(Data.Functor.Classes.Show1 IO)没有实例 因使用“打印”而产生 在交互式 GHCi 命令的 stmt 中:打印它

我该如何解决这个问题?编写这段代码时,我使用了关于 Monad Transformers 的wikibook

编辑:当我尝试使用runMaybeT '运行'它时,它会被执行,但它根本不是故障安全的。例如,输入年龄的废话仍然会导致输出类似于

Person {name: 85, age: *** Exception: Prelude.read: no parse.

【问题讨论】:

  • 你是如何“执行”它的?你用runMaybeT了吗?
  • 不,我不知道,我必须调用 runMaybeT... 但我现在尝试了,它并没有解决真正的问题(请参阅编辑)。
  • 你应该告诉我们Person的定义
  • 你可能想使用readMaybe
  • ps。请注意,您正在提升 do 块内的每个操作。在实际的 Maybe monad 中,你永远不会做任何事情。

标签: haskell io monad-transformers maybe


【解决方案1】:

如果您仅在要求所有输入后才进行验证,我将只使用 IO monad 并返回一个 Maybe:

import Text.Read
import Control.Monad.Trans.Maybe
import Control.Monad.IO.Class

askPerson :: IO (Maybe Person)
askPerson = do
  name <- putStr "Name? " >> getLine
  a <- putStr "Age? " >> getLine
  g <- putStr "Gender? " >> getLine
  return $ do age <- readMaybe a
              gender <- readMaybe g
              return $ Person name age gender

注意我们如何在 return 语句中使用 Maybe monad。

如果您想在输入无效值后退出询问输入,我会使用 MaybeT --

askPersonT :: MaybeT IO Person
askPersonT = do
  name   <- liftIO $ putStr "Name? " >> getLine
  age    <- MaybeT $ fmap readMaybe $ putStr "Age? " >> getLine
  gender <- MaybeT $ fmap readMaybe $ putStr "Gender? " >> getLine
  return $ Person name age gender

doit = runMaybeT askPersonT

如果用户输入的年龄无效,则不会要求他们输入性别。

【讨论】:

  • 我认为,首先,return (Person &lt;$&gt; pure name &lt;*&gt; readMaybe a &lt;*&gt; readMaybe g) 也是惯用的。在第二个中,我们也可以使用 applicative-style,但实际上我更喜欢你写的那样,因为每一行都已经很重要了。
  • 你评论中的纯粹让我感到困惑。我们不是已经在 return 和 fmap 的 usr 中处于正确的上下文中了吗?
  • 哦,我现在明白了。我错过了 readMaybe 调用发生的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-26
  • 1970-01-01
  • 2012-10-05
相关资源
最近更新 更多