Как работает равенство для числовых типов?

Я вижу, что Haskell позволяет сравнивать разные числовые типы:

*Main> :t 3
3 :: Num t => t
*Main> :t 3.0
3.0 :: Fractional t => t
*Main> 3 == 3.0
True

Где находится исходный код Eq экземпляр для числовых типов? Если я создаю новый тип, такой как ComplexNumber, я могу расширить == работать на это? (Я мог бы хотеть, чтобы комплексные числа без мнимых частей были потенциально равны действительным числам.)

2 ответа

Решение

"Haskell позволяет сравнивать различные числовые типы", нет, это не так. На самом деле Хаскелл допускает, чтобы разные типы определялись одними и теми же литералами. В частности, вы можете сделать

Prelude> let a = 3.7 :: Double
Prelude> let b = 1   :: Double
Prelude> a + b
4.7

OTOH, если бы я объявил их явно с конфликтующими типами, добавление не получилось бы:

Prelude> let a = 3.7 :: Double
Prelude> let b = 1   :: Int
Prelude> a + b

<interactive>:31:5:
    Couldn't match expected type ‘Double’ with actual type ‘Int’
    In the second argument of ‘(+)’, namely ‘b’
    In the expression: a + b

Сейчас, Double не самый общий возможный тип для a или же b, На самом деле все числовые литералы полиморфны, но прежде чем произойдет какая-либо операция (например, сравнение на равенство), такой полиморфный тип должен быть привязан к конкретному мономорфному. Подобно,

Prelude> (3.0 :: Double) == (3 :: Double)
True

Так как ==вопреки вашему предположению фактически требует, чтобы обе стороны имели одинаковый тип, вы можете опустить подпись с любой стороны, ничего не меняя:

Prelude> 3.0 == (3 :: Double)
True

На самом деле, даже без каких-либо аннотаций типов, GHCi будет по-прежнему рассматривать обе стороны как Double, Это из-за типа по умолчанию - в данном конкретном случае, Fractional является самым сильным ограничением на тип общего номера, и для Fractionalтип по умолчанию Double, OTOH, если бы обе стороны были целочисленными литералами, то GHCi выбрал бы Integer, Это может иногда иметь значение, например

Prelude> 10000000000000000 == 10000000000000001
False

но

Prelude> 10000000000000000 ==(10000000000000001 :: Double)
True

потому что в последнем случае последний 1 теряется в ошибке с плавающей точкой.

Здесь нет ничего волшебного. 3 может быть как действительным числом, так и целым числом, но его тип объединяется с действительным числом по сравнению с 3.0,

Обратите внимание на тип класса:

class Eq a where
  eq :: a -> a -> Bool

Так eq на самом деле сравнивает только вещи одного типа. И твой пример 3 == 3.0 объединяет свои типы и становится 3.0 == 3.0 внутренне.

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

Другие вопросы по тегам