Почему эта функция не выполняет проверку типа?
Во время лекции по функциональному программированию мы увидели следующую функцию Хаскелла:
f :: Bool -> Int -> (a -> Int) -> Int
f x y z = if x then y + y else (z x) + (z y)
Ожидается, что эта функция не сможет выполнить проверку типа. Однако причина, по которой это происходит, не была объяснена. Когда я попробовал его в GHCI, я получил следующий результат:
Prelude> :l test [1 of 1] Compiling Main ( test.hs,
interpreted )
test.hs:2:35:
Couldn't match expected type `a' with actual type `Bool'
`a' is a rigid type variable bound by
the type signature for f :: Bool -> Int -> (a -> Int) -> Int
at test.hs:1:6
Relevant bindings include
z :: a -> Int (bound at test.hs:2:7)
f :: Bool -> Int -> (a -> Int) -> Int (bound at test.hs:2:1)
In the first argument of `z', namely `x'
In the first argument of `(+)', namely `(z x)' Failed, modules loaded: none.
Почему это происходит?
3 ответа:
Сигнатура типа утверждает, что наша функцияf :: Bool -> Int -> (a -> Int) -> Int f x y z = if x then y + y else (z x) + (z y)zполиморфна в своем первом аргументе. Он принимает значение любого типаaи возвращает значениеInt. Однако область видимости переменной типаaтакже означает, что она должна быть того же типаaпри всех видах использования.aнельзя создавать экземпляры для разных типов на одном и том же сайте использования. Это "полиморфизм 1 ранга".Вы можете прочитать тип как на самом деле:
f :: forall a. Bool -> Int -> (a -> Int) -> IntИтак:
z (x :: Bool) + z (y :: Int)Является недопустимым, так как
aограничен два разных, независимых типа.Расширение языка позволяет нам изменять область действия
aтаким образом, чтобы она могла быть инстанцирована в полиморфную переменную - т. е. содержать различные типы на одном и том же сайте использования, в том числе иметь полиморфные типы функций:Prelude> :set -XRankNTypes Prelude> let f :: Bool -> Int -> (forall a . a -> Int) -> Int f x y z = if x then y + y else (z x) + (z y)Теперь Тип
aне имеет глобальной области действия, и отдельные экземпляры могут отличаться. Это позволяет нам написать" более полиморфную " функциюfи использовать ее:Prelude> f True 7 (const 1) 14Так вот как круто выше рангом полиморфизм есть. Больше повторного использования кода.
Это просто не так, как работает простой параметрический полиморфизм. Функция
zполиморфна по сигнатуре функции, но в теле она только мономорфна.При проверке типа определения средство проверки типа выводит мономорфный тип для переменной типа
aдля использования во всем определении функции. Однако вашfпытается вызватьzс двумя различными типами, и таким образом средство проверки типов выводит два конфликтующих типа дляa.
Даже
f :: Bool -> Int -> (a -> Int) -> Int f x y z = if x then y + y else (z y) + (z y)Не будет проверять тип (как было указано в комментариях) и генерировать ту же ошибку, потому что Хаскелл выводит наименее общие типы для выражений, и ваш тип более общий, чем выводимый. Как говорит "нежное введение в Хаскелла",
Главный тип выражения или функции-это наименее общий тип, который интуитивно "содержит все экземпляры выражения".
Если указать тип в явном виде Хаскелл предполагает, что вы сделали это по какой-то причине, и настаивает на сопоставлении выводимого типа с данным типом.
Для выражения выше выводимый тип является
Аналогично, если бы у вас был только один(Num t) => Bool -> t -> (t -> t) -> t, поэтому при сопоставлении типов он видит, что вы далиIntдляyи типzстановится(Int -> Int). Который является менее общим, чем(a -> Int). Но Вы настаивали на том, чтобы иметьaтам (а неInt) - жесткую переменную типа. Другими словами, ваша функцияfможет принимать только функции типаInt -> Int, но вы настаиваете на том, что ему может быть дана любая функция:: a -> Int, включая:: String -> Intи т. д. (как указывает @augustsson в комментариях). Ваш объявленный тип слишком широк.(z x), он соответствовал бы данному типуxи достиг бы более узкого, чем объявленный тип(Bool -> Int)для функцииz. И еще раз пожаловаться на жесткий тип переменной.Фактически вы объявили тип
(Num t) => Bool -> t -> (t1 -> t) -> t, но на самом деле это(Num t) => Bool -> t -> (t -> t) -> t. Оно это другой тип.