我该如何重写以下代码,以便它:


使用更少的字符
最多包含一个case ... of ...




-- parseSQL :: String -> Either ParseError SQL
-- evalSQL :: SQL -> IO (Either EvalError Table)
-- prettyPrintTable :: Table -> IO ()
-- ParseError, EvalError and Table are instances of Show

evalAndPrint :: String -> IO ()
evalAndPrint x =
    case parseSQL x of
      (Left parseErr) ->
        print parseErr
      (Right sql) -> do
        result <- evalSQL sql
        case result of
          (Left err) ->
            print err
          (Right table) -> do
            prettyPrintTable table
            putStrLn $ "(" ++ show (length table) ++ " lines)\n"

最佳答案

目前,让我们假设您已将parseSQLevalSQL函数泛化为具有这些类型(稍后,即使您无法访问其源代码,我们也会看到如何将您的专用实现转换为泛化的实现) :

parseSQL :: MonadError ParseError m => String -> m SQL
evalSQL :: (MonadError EvalError m, MonadIO m) => SQL -> m Table


然后我们可以写:

-- if we were really doing this the mtl way, we'd introduce a new
-- type class for changing error types instead of specializing to
-- ExceptT, but that's another answer
evalAndThrow :: String -> ExceptT String IO ()
evalAndThrow s = do
    sql   <- withExceptT show (parseSQL s)
    table <- withExceptT show (evalSQL sql)
    liftIO $ prettyPrintTable table
    liftIO . putStrLn $ "(" ++ show (length table) ++ " lines)\n"


顶层函数可以是类似

evalAndPrint s = do
    v <- runExceptT (evalAndThrow s)
    case v of
        Left err -> putStrLn err
        Right _  -> return ()


以下是一些将现有函数转换为mtl样式的多态版本的技巧。您可以直接更改其来源,也可以使用如下组合器制造适配器:

-- this is a generally useful combinator
liftEither :: MonadError e m => Either e a -> m a
liftEither = either throwError return

-- this is a combinator specific to your question
liftIOEither :: (MonadError e m, MonadIO m) => IO (Either e a) -> m a
liftIOEither = join . liftIO . liftM liftEither


当然还有ExceptT :: IO (Either e a) -> ExceptT e IO a; ExceptT . evalSQL的多态性不如liftIOEither . evalSQL,但是由于我们在ExceptT类型中使用它,因此在这种情况下可能无关紧要。

关于haskell - 如何从此代码中删除“case of”?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28857745/

10-10 19:01