Я вижу, что Haskell позволяет сравнивать различные числовые типы:
*Main> :t 3
3 :: Num t => t
*Main> :t 3.0
3.0 :: Fractional t => t
*Main> 3 == 3.0
True
Где исходный код экземпляра Eq для числовых типов? Если я создам новый тип, такой как ComplexNumber, могу ли я расширить == для его работы? (Я мог бы захотеть, чтобы комплексные числа без мнимых частей были потенциально равны действительным числам.)
Ответ 1 · рейтинг 10 · leftaroundabout
"Haskell позволяет сравнивать различные числовые типы", нет, это не так. На самом деле 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 теряется при ошибке с плавающей запятой.
Ответ 2 · рейтинг 2 · clinux
Ничего волшебного здесь не происходит. 3 может быть либо вещественным числом, либо целым числом, но его тип унифицируется с действительным числом по сравнению с 3.0.
Обратите внимание на класс типа:
class Eq a where
eq :: a -> a -> Bool
Так что eq действительно сравнивает только вещи одного типа. И ваш пример 3 == 3.0 получает унифицированные типы и становится 3.0 == 3.0 внутри.
Я не уверен в каких-либо трюках с типом, чтобы сделать его унифицированным с определенным пользователем типом комплексного числа. Моя интуиция подсказывает мне, что это невозможно.