Как выражение `return` имеет тип, отличный от определения функции? - PullRequest
0 голосов
/ 22 июня 2019

Внутри цикла целые числа собираются внутри списка, и кортеж этих целых чисел равен return ed. Как это изменить в список кортежей?

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 Ответ

4 голосов
/ 22 июня 2019

Давайте перепишем ваш код с явными разделителями, чтобы сделать структуру кода более очевидной:

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, и все подходит.

...