Не удалось вывести (уравнение a) из контекста (…)

#haskell #types #typeclass

#haskell #типы #класс типов

Вопрос:

Я новичок в Haskell. Я написал этот код:

 deleteDuplicates :: [a] -> [a]
deleteDuplicates [] = []
deleteDuplicates (x:xs)
        | x == (head xs)        = x : (deleteDuplicates (tail xs))
        | otherwise             = x : (head xs) : (deleteDuplicates (tail xs))
  

Что означает эта ошибка и почему она возникла? Я случайно как-то сравниваю два разных типа?

 set2.hs:10:3:
    Could not deduce (Eq a) from the context ()
      arising from a use of `==' at set2.hs:10:3-16
    Possible fix:
      add (Eq a) to the context of
        the type signature for `deleteDuplicates'
    In the expression: x == (head xs)
        In a stmt of a pattern guard for
                 the definition of `deleteDuplicates':
          x == (head xs)
    In the definition of `deleteDuplicates':
        deleteDuplicates (x : xs)
                           | x == (head xs) = x : (deleteDuplicates (tail xs))
                           | otherwise = x : (head xs) : (deleteDuplicates (tail xs))
  

Ответ №1:

Ваша подпись типа неверна:

 deleteDuplicates :: [a] -> [a]
  

Это говорит о том, что ваша функция может работать со списками любого типа, a но это неправда! Позже вы вызываете:

 x == (head xs)
  

Таким образом, вы должны иметь возможность сравнивать свой тип для равенства. Это означает, что подпись должна быть:

 deleteDuplicates :: Eq a => [a] -> [a]
  

В такие моменты полезно удалить вашу явную подпись типа, загрузить функцию в GHCi и выяснить, какой тип, по мнению интерпретатора, она должна иметь (через :t deleteDuplicates ).

Еще ошибки

Кроме того, ваше использование head there — плохая идея. head является частичной функцией и завершится ошибкой, когда xs == [] . Я предлагаю вам расширить соответствие шаблону:

 deleteDuplicates (x1:x2:xs)
    | x1 == x2 = ...
    | otherwise = x1 : deleteDuplicates (x2:xs)
  

Также обратите внимание на исправление в otherwise случае. Вы пропустили x2, но что, если x2 соответствует следующему элементу в списке? Что-то подобное [1,2,2,3] было бы обнаружено 1 /= 2 , и тогда рекурсия получила бы список [2,3] — упс!

Комментарии:

1. Вы полностью правы насчет чего-то вроде [1, 2, 2, 3] неспособности создать список со всеми удаленными дубликатами. Виноват. Eq a => Для меня это ново … значит, единственное, что нужно сделать, это обеспечить, чтобы элементы типа a были сопоставимы?

2. Питер: В принципе, да. Это «ограничение класса типов». Функция == объявлена в Eq классе типов, поэтому ее можно использовать только в экземплярах Eq.

3. @Pieter: сопоставимо по равенству, да. Eq a => говорит, что a это должен быть экземпляр класса type Eq . Класс типа определяет интерфейс, который реализует экземпляр. Eq Класс предоставляет две функции, (==) и (/=) , обе с типом a -> a -> Bool . Другие классы типов будут предоставлять другие функции, например Ord предоставляет тесты с меньшим, большим, чем и аналогичные тесты упорядочения.

Ответ №2:

Компилятор сообщает вам, что только из сигнатуры типа не может быть определено, a является ли это instanceof Eq .

 deleteDuplicates :: Eq a => [a] -> [a]
  

Вам нужно сообщить компилятору, что a это экземпляр класса типа Eq, упомянув его в подписи.

Ответ №3:

Выражение

 x == (head xs)
  

требуется, чтобы == было определено для членов xs , что означает a , что это должен быть экземпляр класса типов Eq . Измените сигнатуру типа вашей функции на

 deleteDuplicates :: Eq a => [a] -> [a]