return语句与函数定义的类型如何不同?

时间:2019-06-22 09:15:47

标签: haskell types type-inference io-monad do-notation

在一个循环中,整数被收集在一个列表内,并且这些整数的元组被return加起来。这如何更改为元组列表?

input :: IO [(Int,Int)]
input = do
  n <- readLn :: IO Int
  forM [1..n] $ \_ -> do
    [x,y] <- map read . words <$> getLine
    return (x,y)

我期望值​​的类型为(Int,Int),但它为[(Int,Int)]。为什么?

1 个答案:

答案 0 :(得分:4)

让我们使用显式分隔符重新编写代码,使代码结构更加自如:

input :: IO [(Int,Int)]
input = do {
    n <- readLn ;
    forM [1..n] (\ _ -> do {
        [x,y] <- fmap (map read . words) getLine ;
        return (x,y)  })
    }

因此return (x,y)属于内部do

因为那里有一个getLine :: IO String,所以内部do的类型是IO (t1,t2),其中x :: t1, y :: t2。因此,这也是参与forM调用的 lambda函数的返回类型。

forM :: Monad m => [a] -> (a -> m b) -> m [b]起,我们在这里知道m ~ IO,我们得出了总体do的最后一个表达式的类型为

forM :: [a] -> (a -> IO b) -> IO [b]

,因此根据该IO [b] ~ IO [(t1,t2)]表达式,自b ~ (t1,t2)以来,总体类型为return

lambda函数返回IO b,因此forM返回IO [b],与上面的类型相同。 do块的类型与其最后一个表达式的类型相同。

该函数的签名说它是IO [(Int,Int)],所以最终t1 ~ Intt2 ~ Int都适合了。