投掷错误后重新运行计算

时间:2017-01-03 16:21:47

标签: haskell monads monad-transformers

我想在Haskell中组合Error Monad和IO Monad。以下是我的代码的摘录:

type AskMonad = ErrorT String IO

askSomething :: AskMonad Direction
askSomething = do
  liftIO $ putStrLn "Choose an element from the list [1,2,3,4]"
  input <- liftIO getLine
  let entry = read input :: Int
  if entry `elem` [1,2,3,4]
  then return $ toEnum entry -- from Int to Direction
  else throwError "Invalid input!"

selectMove :: [Player] -> Board -> IO (Direction, Position)
selectMove players board = do
  direction <- runErrorT (askSomething) `catchError` --What to put here???
  -- Other IO stuff...

我想实现以下行为:

  • 如果askSomething成功,请获取Direction
  • 如果askSomething失败,show错误并再次运行askSomething

但是,为了得到这种行为,我不知道将catchError的第二个参数放在哪里。我认为它应该是以下类型签名:Either String Direction -> AskMonad Direction,但是如何访问需要重新运行的计算(在这种情况下为askSomething)。

1 个答案:

答案 0 :(得分:2)

您现在拥有的代码似乎正在尝试捕获ErrorT String结果中未表示的异常。我的想法是,如果runErrorT导致Left值(“预期”异常),您实际上正在尝试重试。在这种情况下,像retryForever这样的东西可能会起作用:

retryForever :: AskMonad a -> IO a
retryForever action =
  runErrorT action >>= either (\ex -> putStrLn ex >> retryForever action) return

可以像:

一样使用
direction <- retryForever askSomething

P.S。我认为ErrorT已弃用ExceptTExceptT只是更加通用,因此如果您决定转换,转换应该是微不足道的。