Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: Ellen SpertusОтветов: 2

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

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

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

  • Это всего лишь пример (расширенных?) правил по умолчанию в GHCi. crockeea 01.08.2016
  • Здесь может возникнуть путаница: хотя 3 и 3.0 имеют разные полиморфные типы, в выражении 3 == 3.0 им обоим присваивается один и тот же мономорфный тип перед вычислением равенства. Тип (==) говорит, что он должен принимать два аргумента одного типа: Eq a => a -> a -> Bool. Я подозреваю, что неправильное понимание этой тонкости и привело к вопросу. Daniel Wagner 01.08.2016
  • позволяет сравнивать различные числовые типы. Попробуйте это: length "abc" == 3.0. Это работает? n. 1.8e9-where's-my-share m. 01.08.2016
  • @н.м. Это работает в GHCI. Ellen Spertus 01.08.2016
  • Какая версия GHCi? n. 1.8e9-where's-my-share m. 02.08.2016

typeclasshaskell

Ответы

Ответ 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 внутри.

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы