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 Intpotencia de punto flotante y obtener un resultado de tipo Int. Y así, el sistema de tipos le impide hacer esto: (1::Int) ** 0.5produce un error de tipo. Lo mismo vale para (1::Int) ^^ (-1).
Otra forma de decir esto: los Numtipos están cerrados bajo ^(no es necesario que tengan un inverso multiplicativo), los Fractionaltipos están cerrados bajo ^^, los Floatingtipos están cerrados bajo **. Dado que no existe una Fractionalinstancia 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.
IntyInteger. 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 -> Quackdefine lo que esperamos de un pato, y luego cada instancia especifica algo que puede comportarse como un pato.Duck.