En realidad, hay tres operadores de exponenciación: (^)
, (^^)
y (**)
. ^
es exponenciación integral no negativa, ^^
es exponenciación entera y **
es exponenciación de punto flotante:
(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a
La razón es la seguridad de tipos: los resultados de las operaciones numéricas generalmente tienen el mismo tipo que los argumentos de entrada. Pero no puedes elevar una Int
potencia de punto flotante y obtener un resultado de tipo Int
. Y así, el sistema de tipos le impide hacer esto: (1::Int) ** 0.5
produce un error de tipo. Lo mismo vale para (1::Int) ^^ (-1)
.
Otra forma de decir esto: los Num
tipos están cerrados bajo ^
(no es necesario que tengan un inverso multiplicativo), los Fractional
tipos están cerrados bajo ^^
, los Floating
tipos están cerrados bajo **
. Dado que no existe una Fractional
instancia para Int
, no puede elevarlo a una potencia negativa.
Idealmente, el segundo argumento de ^
estaría restringido estáticamente para no ser negativo (actualmente, 1 ^ (-2)
lanza una excepción en tiempo de ejecución). Pero no hay ningún tipo para números naturales en Prelude
.
Int
yInteger
. Para poder tener esas tres declaraciones de instancia, la resolución de la instancia debe usar el retroceso, y ningún compilador de Haskell implementa eso.No define dos operadores, ¡define tres! Del Informe:
Esto significa que hay tres algoritmos diferentes, dos de los cuales dan resultados exactos (
^
y^^
), mientras que**
dan resultados aproximados. Al elegir qué operador usar, elige qué algoritmo invocar.fuente
^
requiere que su segundo argumento sea unIntegral
. Si no me equivoco, la implementación puede ser más eficiente si sabe que está trabajando con un exponente integral. Además, si quieres algo como2 ^ (1.234)
, aunque tu base sea una integral, 2, tu resultado obviamente será fraccionario. Tiene más opciones para que pueda tener un control más estricto sobre qué tipos entran y salen de su función de exponenciación.El sistema de tipos de Haskell no tiene el mismo objetivo que otros sistemas de tipos, como C, Python o Lisp. La mecanografía de pato es (casi) lo opuesto a la mentalidad de Haskell.
fuente
class Duck a where quack :: a -> Quack
define lo que esperamos de un pato, y luego cada instancia especifica algo que puede comportarse como un pato.Duck
.