Почему Haskell интерпретирует мой тип Num как Enum?

Я пытаюсь скомпилировать следующую функцию в Haskell для имитации дифференцирования многочлена, константы которого указаны в числовом списке:

diff :: (Num a) => [a] -> [a]
diff [] = error "Polynomial unspecified"
diff coeff = zipWith (*) (tail coeff) [0..]

Haskell отказывается его компилировать по следующей причине:

Could not deduce (Enum a) from the context (Num a)
 arising from the arithmetic sequence `0 .. ' at fp1.hs:7:38-42
Possible fix:
 add (Enum a) to the context of the type signature for `diff'
In the third argument of `zipWith', namely `[0 .. ]'
In the expression: zipWith (*) (tail coeff) ([0 .. ])
In the definition of `diff':
diff coeff = zipWith (*) (tail coeff) ([0 .. ])

Почему Haskell рассматривает список [0..] как тип Enum и как это исправить. Имейте в виду, что здесь я хочу воспользоваться преимуществами ленивых вычислений, отсюда и бесконечный список.


person Zaid    schedule 07.10.2009    source источник
comment
Не лучше ли diff [] = [] или diff [_] = [0]? В конце концов, взятие четвертого дифференциала x^2+1 должно быть константой 0, а не ошибкой.   -  person ephemient    schedule 07.10.2009
comment
@ephemient: Ты зоркий! Я позаботился об исключении из списка синглетонов после того, как опубликовал проблему. Код также должен читаться как [1..], а не [0..], чтобы он правильно различался.   -  person Zaid    schedule 07.10.2009


Ответы (4)


[0..] — это синтаксический сахар для enumFrom 0, определенный в классе Enum. Поскольку вы хотите сгенерировать список a с [0..], компилятор требует, чтобы a находился в классе Enum.

Вы можете либо добавить Enum a к сигнатуре типа функции, либо обойти это, сгенерировав [0..] :: [Integer] и используя fromInteger (который определен в классе Num), чтобы получить из него [a]:

diff :: (Num a) => [a] -> [a]
diff [] = error "Polynomial unspecified"
diff coeff = zipWith (*) (tail coeff) (map fromInteger [0..])
person sth    schedule 07.10.2009
comment
Хм, наверное, я бы написал (iterate (+1) 0), так короче, но в итоге все равно... - person ephemient; 07.10.2009
comment
Интересно. Я думал, что это просто бесконечный список целых чисел. То, чему ты учишься... - person Zaid; 07.10.2009
comment
Самое смешное, что всю проблему можно было бы решить, просто исключив сигнатуру типа и позволив машине логического вывода делать свою работу. - person Dario; 07.10.2009
comment
Вы также можете делать такие вещи, как ['a'..'z'] или [1.0, 1.1 .. 2.0], поскольку Char и Float имеют Enum экземпляров... - person sth; 07.10.2009

Правильный тип diff должен быть

diff :: (Num a, Enum a) => [a] -> [a]

потому что использование [x..] требует, чтобы тип создавал экземпляр Enum.

person Dario    schedule 07.10.2009

[0..] — это сокращение от enumFrom 0 см. здесь

person newacct    schedule 07.10.2009

Вот краткий обзор того, что видит компилятор, когда смотрит на эту функцию:

  • [0..] — это список вещей, которые имеют экземпляры Num и Enum. Это должно быть Num из-за «0», и это должно быть Enum из-за «..»
  • Меня просят применить (*) к элементам coeff и [0..] один за другим. Поскольку оба аргумента для (*) должны быть одного типа, а [0..] имеет экземпляр для Enum, coeff также должен иметь экземпляр для Enum.
  • Ошибка! В сигнатуре типа diff упоминается только то, что у coeff есть экземпляр для Num, но я уже определил, что у него должен быть как минимум экземпляр и для Enum.
person Michael Steele    schedule 07.10.2009
comment
Это объясняет, почему другая функция не выдавала эту ошибку. Эта функция включала простой zip: (a,b) <- zip coeff [0..] возвращает кортежи, которые могут иметь смешанные типы данных. - person Zaid; 07.10.2009