不同类型的函数和`in do statement`

时间:2016-10-29 22:58:05

标签: haskell

main :: IO ()
main = do
  args <- getArgs
  case args of
    [] -> putStrLn "Give an argument!"

它有效,但为什么?返回类型为IO ()getArgs返回IO [String](不是IO ())。所以它应该引发一个错误。

type C = ErrorT String IO
main :: C ()
main = do
    args <- getArgs
    case args of
      [] -> putStrLn "Give an argument!"

putStrLn尝试返回IO ()。但它与C不兼容。那么,我应该liftIO吗?我明白我应该。你能告诉我liftIO在这里是如何运作的吗?为什么我必须将liftIOgetArgs一起使用?

2 个答案:

答案 0 :(得分:6)

要弄清楚发生了什么,你可以试着去除你的功能:

main :: IO ()
main = getArgs >>= \ args ->
    case args of
        [] -> putStrLn "Give an argument!"

此处,getArgs的类型为IO [String],函数(我们称之为f)的类型为[String] -> IO ()。现在让我们看看(>>=)

发生了什么
(>>=) :: m a -> (a -> m b) -> m b

-- a unifies with [String] and m with IO
(getArgs >>=) :: ([String] -> IO b) -> IO b
(>>=) :: IO [String] -> ([String] -> IO b) -> IO b

-- b unifies with ()
getArgs >>= f :: IO ()
(>>=) :: IO [String] -> ([String] -> IO ()) -> IO ()

对于C的示例,有三个错误:

  • GHC希望main具有IO ()类型,但在您的代码段中有C ()类型
  • getArgs的格式应为C [String],但类型为IO [String]
  • {li>同样适用于putStrLn,其类型必须为C ()

要解决这个问题,由于ErrorT是一个monad变换器,你可以lift使用lift liftIO对变换器级别进行monadic动作。

liftIO操作,只能应用monad转换liftIO。有趣的是,它可以与任何monad变换器一起使用(具有尽可能多的monad变换器深度):使用T1 (T2 (... (Tn IO)...)) a,您可以使用n处理任何Tn lift :: (MonadTrans t, Monad m) => m a -> t m a liftIO :: MonadIO m => IO a -> m a 1}}是monad变形金刚。

您可以注意到他们的类似签名:

liftIO

您不需要在liftIO的签名中为m指定IO,因为它的MonadIO实例已经告诉我们它只能执行IO操作。

总而言之,这是您在程序中使用type C = ErrorT String IO main :: IO () main = runErrorT go >>= print -- print the result of type `Either String ()` where go :: C () go = do args <- liftIO getArgs case args of [] -> liftIO $ putStrLn "Give an argument!" 的方式:

ErrorT

但是,在这里,因为你没有使用ErrorT monad的任何功能,所以代码并不是一个很好的例子。

另请注意,如果您在mtl包中讨论ExceptT,则不推荐使用此版本,请考虑使用lift

有关liftIO vs import argparse def make_basic_parser(): parser = argparse.ArgumentParser(description='Basic parser (only username/password).', add_help=False) parser.add_argument('--username', type=str, help='Username to access database') parser.add_argument('--password', type=str, help='Password to access database') return parser def alter_table_parser(): parent = make_basic_parser() parser = argparse.ArgumentParser(description='Alter a table', parents=[parent]) parser.add_argument('--table', type=str, help='Table to alter') return parser if __name__ == "__main__": parser = alter_table_parser() args = parser.parse_args() print("Received arguments: %s" % (args))

的详情,请查看this post

答案 1 :(得分:4)

如果你去了do符号:

main = getArgs >>= (\ args -> case args of [] -> putStrLn "Argh!")

getArgs的类型为IO [String]>>=将结果[String]输入lambda,返回IO ()putStrLn "..."的结果。这里,>>=具有推断类型:

(>>=) :: IO [String] -> ([String] -> IO ()) -> IO ()

因此整个表达式的类型为IO (),与main的签名相匹配。