Предполагается, что компилятор может сделать вывод: попробуйте удалить сигнатуру типа функции и посмотрите.Например, с ghci
:
Prelude> :set -XDatatypeContexts
Prelude> data Ord a => OrderedValue a = OrderedValue a
Prelude> let getOrderedValue (OrderedValue a) = a
Prelude> :t getOrderedValue
getOrderedValue :: Ord t => OrderedValue t -> t
Однако, явно указав тип для вашей функции, вы отключите любой вывод.Вывод типа включается, когда нет явной аннотации типа для термина.Если вы явно дадите его, это тип, который термин не будет иметь значения, поэтому он должен быть правильным.
Итак, компилятор здесь проверка типов ваш код, иобнаруживает, что тип, такой как OrderedValue a -> a
, неверен, потому что игнорирует ограничение Ord a
.
Однако обратите внимание, что DatatypeContexts
, который позволяет накладывать ограничение на тип данных, является устаревшим.Консенсус по этому поводу заключается в том, что лучше ставить ограничения только на те функции, которые действительно в них нуждаются.Если вы удалите ограничение Ord a
из типа данных, то функция getOrderedValue
скомпилируется хорошо, потому что не будет никакого ограничения Ord a
для повиновения, и вы действительно можете заметить, что это более близко соответствует интуиции позади исходного типаВы написали, что теперь правильно.
В качестве отступления, обратите внимание, что тип, который автоматически определяет компилятор, является наиболее общим типом для конкретного тела функции для правильной проверки типа., но вы можете явно указать менее общий тип.
Например, следующая функция:
mysum = foldr (+) 0
имеет тип (Num b, Foldable t) => t b -> b
, но вы можете написать:
mysum :: [Integer] -> Integer
mysum = foldr (+) 0
, потому что это правильный тип для функции, хотя более специфичный .Конечно, после того, как конкретный тип назначен на mysum
, вы не можете вызывать его с другим экземпляром Foldable
или другим Num
типом: вы теряете общность, когда специализируете тип.