Int vs Word В общем использовании?

похоже, что общий шаблон принятия / возвращения Int (ie ByteString.hGet и Data.List.length) противоречит шаблону Haskell использования типов strongly-descrbing, поскольку многие из этих случаев могут обрабатывать только положительные числа. Не лучше ли использовать Word, или есть причина, по которой эти функции частично на Int?

3 ответов


верно, что выразительность системы типов Haskell побуждает пользователей назначать точные типы сущностям, которые они определяют. Однако опытные Хаскеллеры с готовностью признают, что необходимо соблюдать баланс между предельной точностью типа (которая, кроме того, не всегда достижима, учитывая текущие пределы системы типа Haskell) и удобством. Короче говоря, точные типы полезны только в определенной степени. За пределами этого момента они часто просто вызывают дополнительную бюрократию для little to no прибыль.

проиллюстрируем проблему на примере. Рассмотрим факторную функцию. Для всех n больше 1, факториал n является четным числом,и факториал 1 не очень интересен, поэтому давайте проигнорируем это. Поэтому, чтобы убедиться, что наша реализация факториальной функции в Haskell верна, у нас может возникнуть соблазн ввести новый числовой тип, который может представлять только беззнаковые четные целые числа:

module (Even) where

newtype Even = Even Integer

instance Num Even where
  ...
  fromInteger x | x `mod` 2 == 0 = Even x
                | otherwise = error "Not an even number."

instance Integral Even where
  ...
  toInteger (Even x) = x

мы запечатайте этот тип данных внутри модуля, который не экспортирует конструктор, чтобы сделать его абстрактным и сделать его экземпляром всех соответствующих классов типов, которые Int - это экземпляр. Теперь мы можем дать факториалу следующую подпись:

factorial :: Int -> Even

тип factorial конечно, более точно, чем если бы мы просто сказали, что он возвращает Int. Но вы найдете, что Defining factorial С таким типом действительно довольно раздражает, потому что вам нужна версия умножения, которая умножает (четное или нечетное)Int С Even производит и Even. Более того, вам может потребоваться ввести посторонние вызовы toInteger в результате вызова factorial в коде клиента, который может быть значительным источником помех и шумов мало приобрести. Кроме того, все эти функции преобразования потенциально могут оказать негативное влияние на производительность.

другая проблема заключается в том, что при введении нового, более точного типа вам часто приходится дублировать всевозможные библиотечные функции. Например, если ввести тип List1 a непустых списков, то вам придется переопределить многие из функций, которые Data.List уже обеспечивает, но для [a] только. Конечно, можно затем сделать эти методы функций ListLike тип класса. Но вы быстро заканчиваете все виды классов типа adhoc и других шаблонных, с снова не большим выигрышем.

последний момент заключается в том, что не следует рассматривать Word быть неподписанным вариант Int. The отчет Хаскелл оставляет фактический размер Int не указано, и только гарантирует, что этот тип должен быть способен представлять целые числа в диапазоне [- 229, 229 - 1]. Тип Word написано целых чисел без знака неуказанной ширину. Не гарантируется, что в любой соответствующей реализации ширина a Word соответствует ширине Int.

хотя я защищая от чрезмерного распространения типа, Я признаю, что введение типа Natural из naturals может быть хорошо. В конечном счете, однако, должен ли Haskell иметь выделенный тип для натуральных чисел, в дополнение к Int, Integer, и различных Word* типы, в основном это вопрос вкуса. И нынешнее положение дел, вероятно, в значительной степени является просто случайностью истории.


то же самое рассуждение применяется, как и в C. Причина использования более точных типов заключается в предотвращении ошибок. Ошибки, в данном случае, такие как попытка использовать отрицательные числа там, где они не имеют смысла. Но поведение Word on over-или underflow, начиная с unsigned int в C, чтобы обернуть вокруг. Если вы попытаетесь использовать отрицательное число, где Word (или ), вы не получите компилятор, кричащий на вас, или даже исключение во время выполнения. Вы получаете большое положительное число. Что вы не имеют возможности отличить от любого другого ("законного") большого положительного числа!

взгляните на это:

Prelude Data.Word> -1 :: Word
4294967295

вместо того, чтобы делать ошибки нельзя совершать, их невозможно обнаружить. С Intint), у вас, по крайней мере, есть возможность проверить отрицательные значения вручную. С Word и unsigned int, у вас ничего нет.

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

* по крайней мере, x86 требует дополнительной инструкции -- после каждой операции! -- чтобы проверить, произошло ли переполнение или утечка. Я не знаю, есть ли архитектура, которая делает это "бесплатно", хотя было бы неплохо. Или, может быть, отличительная ценность NaN как и для чисел с плавающей запятой (возможно, вместо самое отрицательное число), который будет использоваться для обозначения непредставимых значений...


мое первое предположение, что беззнаковая арифметика имеет некоторые проблемы, которые вызовут глупые ошибки, если вы не обращаете внимания:

Prelude Data.Word> let x = 0 :: Word in if x - 1 > x then "Ouch" else "Ohayoo"
"Ouch"

у него будут некоторые проблемы с полиморфными функциями, которые кажутся правильными:

Prelude Data.Word> let f acc x y = if x <= y then f (acc + 1) x (y - 1) else acc
Prelude Data.Word> :t f
f :: (Num a1, Num a, Ord a) => a1 -> a -> a -> a1

С помощью стандартной функции length:

Prelude Data.Word> let len' x = if null x then 0 else 1 + len' (tail x) :: Int
Prelude Data.Word> :t len'
len' :: [a] -> Int

Prelude Data.Word> f 0 0 (len' [1,2,3])
4

и используя функцию длины returing Word:

Prelude Data.Word> let len x = if null x then 0 else 1 + len (tail x) :: Word
Prelude Data.Word> :t len
len :: [a] -> Word

Prelude Data.Word> f 0 0 (len [1,2,3])
...diverges...

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