Я изучаю основы Haskell и с трудом понимаю сообщения компилятора, то есть как типы из сообщений об ошибках соответствуют типам из выражения.
Если я намеренно дам компилятору неправильное выражение
sum map (+1) [1,2,3] -- this is wrong on purpose to trigger a
-- compiler error
вместо правильного
sum (map (+1) [1,2,3])
тогда я хотел бы понять, что означает вывод компилятора.
Например:
:t sum map (+1) [1,2,3]
<interactive>:1:5: error:
• Couldn't match type: [b0]
with: [a2] -> t
Expected: (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
Actual: (a0 -> b0) -> [a0] -> [b0]
• In the first argument of ‘sum’, namely ‘map’
In the expression: sum map (+ 1) [1, 2, 3]
Я обновил ваш вопрос, чтобы было более ясно, что вы знаете, что выражение неверно, но хотите научиться читать вывод компилятора/интерпретатора. Если вам не нравится моя переписка, вы можете вернуться к своему старому вопросу, нажав на историю и отменив мои изменения. Дайте мне знать, если вам нужна помощь с этим, если это необходимо.
Большое спасибо за редактирование, стало намного понятнее.
Вы хотите применить sum
к результату всех остальных, поэтому вам нужны скобки,
sum (map (+1) [1,2,3])
или a $, который сначала оценивает свою правую сторону, прежде чем передать ее левой:
sum $ map (+1) [1,2,3]
Как вы написали, вы применяете sum
к map
, как вы написали (sum map) (+1) [1,2,3]
, что не имеет смысла.
Ошибка просто говорит вам, что есть ошибка
• In the first argument of ‘sum’, namely ‘map’
который (map
) имеет тип Actual
(a0 -> b0) -> [a0] -> [b0]
, тогда как единственный способ, которым компилятор мог бы понять все выражение, - это передать вместо map
сущность типа Expected
(a0 -> b0) -> (a1 -> a1) -> [a2] -> t
.
Этот ожидаемый тип является просто следствием дополнительных аргументов, которые вы передали sum map
. Действительно, sum map
само по себе «хорошо» в том смысле, что в принципе может иметь смысл. Это тип
:: (Foldable ((->) (a -> b)), Num ([a] -> [b])) => [a] -> [b]
где вы уже можете видеть абсурдность ожидания [a] -> [b]
, который является экземпляром Num
.
Но это нормально, потому что типы по-прежнему полиморфны, недостаточно ограничены, чтобы компилятор понял, что это не может работать.
В тот момент, когда вы передаете некоторые типы, которые имеют почти точный тип, например [1,2,3]
, который является Num a => [a]
, и (+1)
, который является Num a => a -> a
, тогда компилятор не может понять это и сообщает вам об этом.
Ваш вопрос можно было бы как-то перефразировать
Почему тип
x
в\x -> sum x (+1) [1,2,3]
— это(Foldable t1, Num a1, Num a2, Num ((a1 -> a1) -> [a2] -> t2)) => t1 ((a1 -> a1) -> [a2] -> t2)
?
И снова ответ заключается в том, что это все, что может сделать компилятор, чтобы попытаться проверить типы.
Кроме того, посмотрите на этот пример,
sum (:) (+1) [1,2,3]
где я передаю (:)
вместо map
. Ошибка немного другая:
<interactive>:37:5: error:
• Couldn't match type: [a0]
with: [a2] -> t
Expected: a0 -> (a1 -> a1) -> [a2] -> t
Actual: a0 -> [a0] -> [a0]
• In the first argument of ‘sum’, namely ‘(:)’
In the expression: sum (:) (+ 1) [1, 2, 3]
Посмотрите, как это соотносится с вашим:
<interactive>:1:5: error:
• Couldn't match type: [b0]
with: [a2] -> t
Expected: (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
Actual: (a0 -> b0) -> [a0] -> [b0]
• In the first argument of ‘sum’, namely ‘map’
In the expression: sum map (+ 1) [1, 2, 3]
В вашем примере компилятор ожидал (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
, тогда как в моем примере он ожидал a0 -> (a1 -> a1) -> [a2] -> t
. Значит, компилятор ожидал другого?! Эти типы действительно разные?
(a0 -> b0) -> (a1 -> a1) -> [a2] -> t
a0 -> (a1 -> a1) -> [a2] -> t
Нет, просто первое может принимать первый аргумент типа a0 -> b0
, то есть функцию, тогда как второе может принимать любые a0
, то есть функции плюс не функции.
И это следствие того факта, что вы передали map
, который является менее общим типом, (a -> b) -> [a] -> [b]
, а я передал (:)
, который является немного более общим, a -> [a] -> [a]
, так что первый в конечном итоге ограничил ожидаемую сигнатуру первого аргумента до sum
немного больше, то есть (a0 -> b0)
вместо a0
.
Теперь становится понятнее, но хотелось бы подробнее остановиться на ожидаемом типе: (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
Так ли это, что в этом типе выражение (a1 -> a1)
соответствует (+1)
, [a2]
— [1,2,3]
, t
— выходной тип функции суммы? И является ли (a0 -> b0)
типом выражения, которое следует использовать вместо карты, чтобы удовлетворить все выражение? Какой может быть пример такой функции, которую мы могли бы использовать здесь в качестве примера?
@AlexScell, нет, тип выражения, которое «следует» использовать вместо map
, - это то, что компилятор говорит вам, как ожидаемый тип, (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
. Я привел еще один пример, когда компилятор, похоже, ожидает другой тип. На самом деле это не так. Подумайте об этом так: компилятор пытается решить систему алгебраических уравнений, которая не имеет решения. Он пытается это сделать, приходит к противоречию после всех возможных упрощений и говорит вам, что не подходит.
большое спасибо за пояснения. Но что я до сих пор не могу до конца понять - ожидаемый тип (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
все же соответствует всему типу выражения sum map (+1) [1,2,3]
? Поскольку ожидаемый тип оканчивается на t, что соответствует полиморфному скаляру, т. е. результату функции суммирования, а [a2] — это список того, что ожидает сумма. Не так ли выглядит ожидаемый тип? sum ((a0 -> b0) -> (a1 -> a1) -> [a2] -> t) (+1) [1,2,3]
. Если это так, то непонятно, откуда берутся промежуточные шаги (a1 -> b1) и другие.
Нет, ошибка ясна, когда вы говорите, что (a0 -> b0) -> (a1 -> a1) -> [a2] -> t
является ожидаемым типом для первого аргумента «суммы», а не для всего выражения.
Спасибо за ваши объяснения и терпение.
f x y z
вызываетf
с тремя аргументами. Точно так жеsum map (+1) [1,2,3]
вызываетsum
с тремя аргументами. Вам нужны скобкиsum (map (+1) [1,2,3])
.