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
在这里是如何运作的吗?为什么我必须将liftIO
与getArgs
一起使用?
答案 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
的示例,有三个错误:
main
具有IO ()
类型,但在您的代码段中有C ()
类型getArgs
的格式应为C [String]
,但类型为IO [String]
putStrLn
,其类型必须为C ()
要解决这个问题,由于ErrorT
是一个monad变换器,你可以lift
使用lift
liftIO
对变换器级别进行monadic动作。
lift
是IO
操作,只能应用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))
答案 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
的签名相匹配。