Давайте перепишем ваш код с явными разделителями, чтобы сделать структуру кода более очевидной:
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
вызове.
Так как 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
.
Лямбда-функция возвращает IO b
, поэтому forM
возвращает IO [b]
в соответствии с ее типом выше. И тип блока do
совпадает с типом его последнего выражения.
Подпись функции говорит о том, что это IO [(Int,Int)]
, поэтому, наконец, t1 ~ Int
и t2 ~ Int
, и все подходит.