Добавление ограничений типа в контекст объявлений экземпляров в Haskell

Я пытаюсь представить взвешенные ребра. В конечном итоге я хочу, чтобы OutE был экземпляром Eq и Ord с ограничением, что etype является экземпляром Eq и Ord. Предположим, у меня есть следующий файл как temp.hs:

data (Ord etype)=> OutE vtype etype = OutE {destVertex:: vtype, edgeValue::etype}

applyFunBy accessor ordfun = (\x y -> (ordfun (accessor x) (accessor y)))

instance Eq (OutE vtype etype) where
    --(==) :: Ord etype => (OutE vtype etype) -> (OutE vtype etype) -> Bool
    --(/=) :: Ord etype => (OutE vtype etype) -> (OutE vtype etype) -> Bool
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

когда я загружаю это в ghci, я получаю следующие ошибки:

temp.hs:10:19:
    Could not deduce (Ord etype)
      from the context (Eq (OutE vtype etype))
      arising from a use of `edgeValue' at temp.hs:10:19-27
    Possible fix:
      add (Ord etype) to the context of the instance declaration
    In the first argument of `applyFunBy', namely `edgeValue'
    In the expression: applyFunBy edgeValue (==)
    In the definition of `==': == = applyFunBy edgeValue (==)

temp.hs:11:19:
    Could not deduce (Ord etype)
      from the context (Eq (OutE vtype etype))
      arising from a use of `edgeValue' at temp.hs:11:19-27
    Possible fix:
      add (Ord etype) to the context of the instance declaration
    In the first argument of `applyFunBy', namely `edgeValue'
    In the expression: applyFunBy edgeValue (/=)
    In the definition of `/=': /= = applyFunBy edgeValue (/=)
Failed, modules loaded: none.

Если включить строки для сигнатур типов для (==) и (\=), я получаю:

temp.hs:6:1:
    Misplaced type signature:
    == ::
      (Ord etype) => (OutE vtype etype) -> (OutE vtype etype) -> Bool

temp.hs:7:1:
    Misplaced type signature:
    /= ::
      (Ord etype) => (OutE vtype etype) -> (OutE vtype etype) -> Bool

person highBandWidth    schedule 17.11.2010    source источник


Ответы (2)


Вы ограничили etype значением Ord в определении OutE:

data (Ord etype) => OutE vtype etype = ...

Но в экземпляре Eq вы фактически пытаетесь определить экземпляр для любого etype без ограничений.

instance Eq (OutE vtype etype) where

Конечно, это не работает, так как сам OutE только что определен для Ord etypes, поэтому вам также придется добавить ограничение класса типов в определение экземпляра.

instance (Ord etype) => Eq (OutE vtype etype) where

Обратите внимание, что для работы класса типов достаточно одного определения == или /=.


Обратите внимание, что часто проще и поэтому считается лучшим стилем не иметь ограничений класса типов для data-типов, а только для экземпляров/методов, которые действительно требуют функциональности класса типов.

Во многих случаях ограничение не требуется, и в результате получаются излишне неуклюжие сигнатуры типов.

Возьмите, например. какой-то упорядоченный тип карты Ord key => Map key value.

Что, если мы просто хотим перечислить все ключи? Или получить количество элементов? Нам не нужны ключи Ord для них, так почему бы просто не оставить карту без ограничений с помощью простого

getKeys :: Map key value -> [key]
getLength :: Map key value -> Int

и просто добавьте класс типов, когда он нам действительно нужен, в такой функции, как

insert :: Ord key => key -> value -> Map key value
person Dario    schedule 17.11.2010

data (Ord etype)=> OutE vtype etype = OutE {destVertex:: vtype, edgeValue::etype}

Первая проблема: это считается плохим стилем. Объявления ваших типов данных не должны иметь ограничений. Оставьте ограничения функциям, как это делает пакет контейнеров.

instance Eq (OutE vtype etype) where

Вторая "проблема". Вы можете просто добавить deriving (Eq) после объявления данных. Я предполагаю, что вы знаете это и пишете экземпляр явно для собственного обучения (хорошо для вас)...

instance Eq (OutE vtype etype) where
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

Третья проблема: вы не можете сравнивать значения эквити, если они относятся к классу Eq. Итак, вы хотите сказать, что etype ограничено уравнением:

instance (Eq etype) => Eq (OutE vtype etype) where
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

В-четвертых, вам на самом деле не нужно писать экземпляры как для (==), так и для (/=). Значения по умолчанию будут работать после того, как вы определите один из них.

person Thomas M. DuBuisson    schedule 17.11.2010
comment
deriving (Eq) будет генерировать операторы равенства на основе всех полей записи (и, таким образом, генерировать громоздкий экземпляр Eq с Eq vtype), тогда как явный экземпляр, указанный в вопросе, просто сравнивает на основе edgeValue. - person Dario; 17.11.2010
comment
Верно, я не замечал, что он делал это, пока не написал этот отрывок. Спасибо что подметил это. - person Thomas M. DuBuisson; 17.11.2010