Добавление ограничений типа в объявления данных - PullRequest
0 голосов
/ 17 октября 2018

Я читал о создании наших собственных типов и классов типов из Learn You a Haskell for Great Good .Я не совсем понимаю поведение компилятора Haskell, когда я добавляю ограничение типа в мои объявления data.

Например, у меня есть

{-# LANGUAGE DatatypeContexts #-}

data (Ord a) => OrderedValue a = OrderedValue a

getOrderedValue :: OrderedValue a -> a
getOrderedValue (OrderedValue a) = a

Как видно выше, У меня есть ограничение типа в моем объявлении данных, утверждая, что любое значение, которое содержится внутри OrderedValue, должно иметь экземпляр Ord.

Я попытался скомпилировать этот код, и компилятор выплюнул

• No instance for (Ord a) arising from a use of ‘OrderedValue’
  Possible fix:
    add (Ord a) to the context of
      the type signature for:
        getOrderedValue :: forall a. OrderedValue a -> a
• In the pattern: OrderedValue a
  In an equation for ‘getOrderedValue’:
      getOrderedValue (OrderedValue a) = a

Изменение определения getOrderedValue на

getOrderedValue :: (Ord a) => OrderedValue a -> a
getOrderedValue (OrderedValue a) = a

, как и ожидалось, решило проблему.

Мой вопрос - почему компилятор жалуется здесь?Я предположил, что компилятор должен иметь возможность сделать вывод, что шаблон a, сопоставляемый с

getOrderedValue (OrderedValue a) = a

, имеет экземпляр Ord, поскольку конструктор значения OrderedValue предназначен для создания экземпляров типа OrderedValueу которого есть параметр типа a, у которого есть Ord экземпляр.

Уф, это было глотком.

Спасибо.

РЕДАКТИРОВАТЬ - Я посмотрел на альтернативные ответы, которые предложил @melpomene, спасибо за это.Однако я ищу ответ, который описывает , почему разработчики языка Haskell решили реализовать его таким образом.

1 Ответ

0 голосов
/ 21 октября 2018

Предполагается, что компилятор может сделать вывод: попробуйте удалить сигнатуру типа функции и посмотрите.Например, с 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 типом: вы теряете общность, когда специализируете тип.

...