Хтось може сказати мені, чому Прелюдія Haskell визначає дві окремі функції для піднесення до степені (тобто ^і **)? Я думав, що система типу повинна була усунути подібне дублювання.
Prelude> 2^2
4
Prelude> 4**0.5
2.0
Хтось може сказати мені, чому Прелюдія Haskell визначає дві окремі функції для піднесення до степені (тобто ^і **)? Я думав, що система типу повинна була усунути подібне дублювання.
Prelude> 2^2
4
Prelude> 4**0.5
2.0
Відповіді:
Є на насправді три оператора зведення в ступінь: (^), (^^)і (**). ^є невід’ємним інтегральним піднесенням до степені, ^^є цілочисельним підрахуванням та **підсиленням із плаваючою комою:
(^) :: (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типи закриваються під **. Оскільки для цього не існує Fractionalекземпляра Int, ви не можете підняти його до від'ємного рівня.
В ідеалі, другий аргумент ^буде статично обмежений як невід’ємний (на даний момент 1 ^ (-2)видає виняток під час виконання). Але в. Немає типу натуральних чисел Prelude.
Система типу Хаскелла недостатньо потужна, щоб виразити три оператори степенізації як один. Що б ви насправді хотіли, це приблизно так:
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 на даний момент.
Intі Integer. Щоб мати можливість мати ці три оголошення екземпляра, роздільна здатність екземпляра повинна використовувати зворотне відстеження, і жоден компілятор Haskell цього не реалізує.
Він не визначає двох операторів - він визначає трьох! З доповіді:
Існує три операції з підсилення з двома аргументами: (
^) піднімає будь-яке число до невід’ємного цілого степеня, (^^) піднімає дробове число до будь-якого цілого степеня і (**) бере два аргументи з плаваючою комою. Значенняx^0абоx^^0дорівнює 1 для будь-якогоx, включаючи нуль;0**yне визначено.
Це означає, що існує три різних алгоритми, два з яких дають точні результати ( ^і ^^), а **дає приблизні результати. Вибираючи, якого оператора використовувати, ви обираєте, який алгоритм використовувати.
^вимагає, щоб другий аргумент був Integral. Якщо я не помиляюся, реалізація може бути більш ефективною, якщо ви знаєте, що працюєте з інтегральним показником. Крім того, якщо ви хочете щось подібне 2 ^ (1.234), хоча ваша база є інтегралом, 2, ваш результат, очевидно, буде дробовим. У вас є більше варіантів, щоб ви могли більш чітко контролювати, які типи входять і виходять з вашої функції піднесення до степені.
Система типу Хаскелла не має тієї ж мети, що й інші системи типів, такі як C, Python або Lisp. Введення качок (майже) протилежне мисленню Хаскелла.
class Duck a where quack :: a -> Quackвизначає, що ми очікуємо від качки, а потім кожен екземпляр визначає щось, що може поводитися як качка.
Duck.