Не удалось вывести (Eq a) из (Num a) или из (Floating a). Но можно вывести (Eq a) из (Integral a). Зачем?

Я прохожу через учебник по haskell (узнаю, что вы haskell для отличного хорошего), и я играл с этим кодом, который я написал, основываясь на одной из функций в книге.

reverseNum :: (Num a) => a -> a
reverseNum 123 = 321
reverseNum x = 0

и ghci говорит мне, что он не может вывести (Eq a) из (Num a).

Итак, я меняю первую строку на эту

reverseNum :: (Integral a) => a -> a

и это сработало. Это было странно, потому что я считал, что я являюсь частью класса Num, который вам нужен, чтобы быть отдельно от Eq.

Я попробовал еще одну вещь, чтобы удовлетворить меня любопытством и изменил первые 2 строки на этот

reverseNum :: (Floating a) => a -> a
reverseNum 1.0 = 0.1

и это дало мне ту же ошибку.

Я знаю, что вы можете исправить это, сделав что-то вроде reverseNum :: (Num a, Eq a) ..., но я хочу знать, почему Integral является единственным, где Eq может быть выведено. Почему это?

P.S. Я действительно новичок в haskell, поэтому... будьте осторожны:)

Ответы

Ответ 1

Короткий ответ

Потому что определение Num в прелюдии:

class Num a where
    ...

В то время как для определения Integral требуется, чтобы тип был Real и Enum:

class (Real a, Enum a) => Integral a where
    ...

И Real подразумевает как Num, так и Ord...

class (Num a, Ord a) => Real a where
    ...

И Ord, естественно, следует Eq:

class Eq a => Ord a where
    ...

Эта строка означает, что для того, чтобы что-то реализовать Ord, она должна также реализовать Eq. Или можно сказать, что Ord является подклассом Eq. В любом случае...

Резюме состоит в том, что Num не является подклассом Eq, но Integral является подклассом Eq.

Длинный ответ (почему?)

Вы можете представить себе реализацию Num способами, которые делают невозможным реализацию Eq.

newtype Sequence = Sequence (Integer -> Integer)

instance Num Sequence where
  (Sequence x) + (Sequence y) = Sequence $ \pt -> x pt + y pt
  (Sequence x) - (Sequence y) = Sequence $ \pt -> x pt - y pt
  (Sequence x) * (Sequence y) = Sequence $ \pt -> x pt * y pt
  negate (Sequence x) = Sequence $ \pt -> -pt
  abs (Sequence x) = Sequence $ \pt -> abs pt
  signum (Sequence x) = Sequence $ \pt -> signum pt
  fromInteger = Sequence . const

-- Ignore the fact that you'd implement these methods using Applicative.

Здесь Sequence - это тип, представляющий все вычислимые последовательности. Вы не можете реализовать Eq любым разумным способом, потому что последовательности бесконечно длинны!

instance Eq Sequence where
  -- This will never return True, ever.
  (Sequence x) == (Sequence y) =
      and [x pt == y pt | pt <- [0..]] &&
      and [x pt == y pt | pt <- [-1,-2..]]

Итак, имеет смысл, что Num не является подклассом Eq, потому что есть полезные типы, которые могут реализовать Num, но не Eq.