3

我正在学习一些介绍性的 Haskell 材料,目前正在学习 Monads。我从概念上理解>>=运算符的类型:

(Monad m) => m a -> (a -> m b) -> m b.

在这种情况下,我很困惑为什么以下代码有效,即为什么它不会导致类型不匹配:

main = getLine >>= \xs -> putStrLn xs

既然我们知道这一点getLine :: IO String,我会假设它可以与 type 的函数“绑定” String -> IO String。然而putStrLn是不同的类型:putStrLn :: String -> IO ().

那么为什么 Haskell 允许我们使用>>=这两个函数呢?

4

3 回答 3

14

让我们排列类型:

(>>=)    ::  m      a -> (     a ->  m  b) -> m b
getLine  :: IO String
putStrLn ::              (String -> IO ())

这里我们有m = IO,a = Stringb = (), 所以我们可以将它们代入>>='s 类型签名以获得最终的类型签名

(>>=) :: IO String -> (String -> IO ()) -> IO ()
于 2014-10-14T16:39:02.790 回答
5

()是一个有效的类型(称为 unit,注意它只包含一个可能的非底部值)并且在定义中是b.

a=Stringb=()因此我们得到:

IO String -> (String -> IO ()) -> IO ()

于 2014-10-14T16:34:40.703 回答
4

既然我们知道这一点getLine :: IO String,我会假设它可以与 type 的函数“绑定” String -> IO String

你为什么那么想?再看一下类型签名:

(>>=) :: m a -> (a -> m b) -> m b

左边是m a,右边是m b。最特别的是中间的a -> m b那位,表示你传递给的函数>>=接受一个a并返回一个m b。它没有说它必须返回m a,它说它可以m bb任何随机类型在哪里。它不必匹配a

在您的示例中, lambda 函数采用 aString并返回 a IO ()。所以a = Stringb = ()。这很好。

于 2014-10-14T16:46:00.653 回答