Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Верно, что выразительность системы типа Haskell побуждает пользователей назначать точные типы сущностям, которые они определяют. Однако опытные Haskellers с готовностью признают, что нужно балансировать между точностью конечного типа (что, кроме того, не всегда достижимо с учетом текущих ограничений системы типа Haskell) и удобством. Короче говоря, точные типы полезны только для точки. Помимо этого, они часто просто заставляют лишнюю бюрократию практически не выигрывать.

Позвольте проиллюстрировать проблему с примером. Рассмотрим факторную функцию. Для всех 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. Но вы обнаружите, что определение factorial с таким типом действительно очень раздражает, потому что вам нужна версия умножения, которая умножает (четный или нечетный) Int на Even и производит и Even. Что еще, вам может потребоваться ввести посторонние вызовы toInteger в результате вызова factorial в код клиента, что может стать значительным источником беспорядка и шума для небольшого усиления. Кроме того, все эти функции преобразования могут потенциально негативно повлиять на производительность.

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

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

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

Ответ 2

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

Посмотрите на это:

Prelude Data.Word> -1 :: Word
4294967295

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

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

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

Ответ 3

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

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

Использование стандартной функции длины:

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

И используя функцию длины, сохраняющую 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 в Int, чтобы использовать его в других обычных местах.