我正在通过一些介绍性的Haskell材料,目前正在通过Monads。我从概念上理解>>=
运算符的类型为:
(Monad m) => m a -> (a -> m b) -> m b
。
在这种情况下,我对以下代码的工作原理感到困惑,即为什么它不会导致类型不匹配:
main = getLine >>= \xs -> putStrLn xs
因为我们知道getLine :: IO String
,所以我假设它可以与String -> IO String
类型的函数“绑定”。但putStrLn
的类型不同:putStrLn :: String -> IO ()
。
那么为什么Haskell允许我们将>>=
与这两个函数一起使用?
答案 0 :(得分:14)
让我们排列类型:
(>>=) :: m a -> ( a -> m b) -> m b
getLine :: IO String
putStrLn :: (String -> IO ())
此处我们有m = IO
,a = String
和b = ()
,因此我们可以将这些内容替换为>>=
类型的签名,以获得最终类型签名
(>>=) :: IO String -> (String -> IO ()) -> IO ()
答案 1 :(得分:5)
()
是一个有效的类型(称为单位,请注意它只包含一个可能的非底值),在定义中将是b
。
a
= String
和b
= ()
因此我们得到:
IO String -> (String -> IO ()) -> IO ()
答案 2 :(得分:4)
由于我们知道
getLine :: IO String
,因此我认为它可能会被绑定'具有String -> IO String
类型的函数。
为什么你会这么想?再看一下类型签名:
(>>=) :: m a -> (a -> m b) -> m b
左边的内容是m a
,右边的内容是m b
。最特别是中间的位a -> m b
,表示您传递给>>=
的函数需要a
并返回m b
。它没有说它必须返回m a
,它说它可以是m b
,其中b
是任意随机类型。它不必匹配a
。
在您的示例中,lambda函数采用String
并返回IO ()
。所以a = String
和b = ()
。那很好。