Почему эта функция не выполняет проверку типа?
Во время лекции по функциональному программированию мы увидели следующую функцию Хаскелла:
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
. Оно это другой тип.