Экспонирование в Хаскеле - PullRequest
82 голосов
/ 19 июня 2011

Может кто-нибудь сказать мне, почему Прелюдия на Haskell определяет две отдельные функции для возведения в степень (то есть ^ и **)? Я думал, что система типов должна была устранить это дублирование.

Prelude> 2^2
4
Prelude> 4**0.5
2.0

Ответы [ 4 ]

121 голосов
/ 19 июня 2011

На самом деле существует три оператора возведения в степень: (^), (^^) и (**). ^ - неотрицательное целочисленное возведение в степень, ^^ - целочисленное возведение в степень, а ** - возведение в степень с плавающей точкой:

(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

Причина заключается в безопасности типов: результаты числовых операций обычно имеют тот же тип, что и входные аргументы. Но вы не можете поднять Int до степени с плавающей запятой и получить результат типа Int. И поэтому система типов не позволяет вам сделать это: (1::Int) ** 0.5 выдает ошибку типа. То же самое касается (1::Int) ^^ (-1).

Другой способ выразить это: Num типы закрываются в ^ (они не обязательно должны иметь мультипликативный обратный), Fractional типы закрываются в ^^, Floating типы закрываются в **. Поскольку для Int нет экземпляра Fractional, вы не можете поднять его до отрицательной степени.

В идеале, второй аргумент ^ должен быть статически ограничен, чтобы быть неотрицательным (в настоящее время 1 ^ (-2) генерирует исключение во время выполнения). Но в натуральных числах типа Prelude.

нет типа.
28 голосов
/ 19 июня 2011

Система типов Haskell недостаточно мощна, чтобы выразить три оператора возведения в степень как один. То, что вы действительно хотите, это что-то вроде:

class Exp a b where (^) :: a -> b -> a
instance (Num a,        Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a,   Floating b) => Exp a b where ... -- current **

Это на самом деле не работает, даже если вы включаете расширение класса с несколькими параметрами, потому что выбор экземпляра должен быть более умным, чем это допускает Haskell.

10 голосов
/ 19 июня 2011

Он не определяет два оператора - он определяет три! Из отчета:

Существует три операции возведения в степень с двумя аргументами: (^) увеличивает любое число до неотрицательной целочисленной степени, (^^) увеличивает дробное число до любой целой степени, а (**) принимает две плавающие точечные аргументы. Значение x^0 или x^^0 равно 1 для любого x, включая ноль; 0**y не определено.

Это означает, что есть три различных алгоритма, два из которых дают точные результаты (^ и ^^), в то время как ** дает приблизительные результаты. Выбирая, какой оператор использовать, вы выбираете, какой алгоритм вызывать.

4 голосов
/ 19 июня 2011

^ требует, чтобы его второй аргумент был Integral. Если я не ошибаюсь, реализация может быть более эффективной, если вы знаете, что работаете с интегральным показателем. Кроме того, если вы хотите что-то вроде 2 ^ (1.234), даже если ваша база является целым числом 2, ваш результат, очевидно, будет дробным. У вас есть больше опций, чтобы вы могли более строго контролировать, какие типы входят и выходят из функции возведения в степень.

Система типов Haskell не имеет той же цели, что и другие системы типов, такие как C, Python или Lisp. Утиная печать (почти) противоположна мышлению Хаскелла.

...