fromintegral haskell

fromintegral haskell



¿Por qué no puedo agregar Integer to Double en Haskell? (4)

¿Por qué es lo que puedo hacer?

1 + 2.0

pero cuando lo intento

let a = 1 let b = 2.0 a + b <interactive>:1:5: Couldn''t match expected type `Integer'' with actual type `Double'' In the second argument of `(+)'', namely `b'' In the expression: a + b In an equation for `it'': it = a + b

¡Esto parece simplemente extraño! ¿Alguna vez te hace tropezar?

PD: Sé que "1" y "2.0" son constantes polimórficas. Eso no es lo que me preocupa. Lo que me preocupa es por qué haskell hace una cosa en el primer caso, pero otra en el segundo.


El primero funciona porque los literales numéricos son polimórficos (se interpretan como fromInteger literal resp. fromRational literal ), por lo que en 1 + 2.0 , realmente tienes fromInteger 1 + fromRational 2 , en ausencia de otras restricciones, el tipo de resultado predeterminado es Double .

El segundo no funciona debido a la restricción de monomorfismo. Si vincula algo sin una firma de tipo y con un enlace de patrón simple ( name = expresion ), a esa entidad se le asigna un tipo monomórfico. Para el literal 1 , tenemos una restricción Num , por lo tanto, de acuerdo con las reglas predeterminadas, su tipo está predeterminado a Integer en el enlace let a = 1 . De forma similar, el tipo de literal fraccional está predeterminado en Double .

Por cierto, funcionará si :set -XNoMonomorphismRestriction en ghci.

El motivo de la restricción de monomorfismo es evitar la pérdida de uso compartido, si ve un valor que parece una constante, no espera que se calcule más de una vez, pero si tuviera un tipo polimórfico, se volvería a calcular cada vez. esta usado.


La firma de tipo de (+) se define como Num a => a -> a -> a , lo que significa que funciona en cualquier miembro de la clase de tipos Num , pero ambos argumentos deben ser del mismo tipo.

El problema aquí es con GHCI y el orden en que establece los tipos, no el propio Haskell. Si pusiera cualquiera de sus ejemplos en un archivo (usando do para las expresiones let ), se compilaría y ejecutaría bien, porque GHC usaría toda la función como contexto para determinar los tipos de literales 1 y 2.0 .

Todo lo que sucede en el primer caso es que GHCI está adivinando los tipos de números que está ingresando. El más preciso es un Double , por lo que solo asume que se suponía que el otro era un Double y ejecuta el cálculo. Sin embargo, cuando usa la expresión let , solo tiene un número para trabajar, por lo que decide que 1 es un Integer y 2.0 es un Double .

EDITAR: GHCI no es realmente "adivinar", está usando reglas predeterminadas de tipo muy específicas que están definidas en el Informe Haskell. Puedes leer un poco más sobre eso here .


Otros han abordado muchos aspectos de esta pregunta bastante bien. Me gustaría decir una palabra sobre la razón detrás de por qué + tiene la firma de tipo Num a => a -> a -> a .

En primer lugar, la clase de tipos Num no tiene forma de convertir una instancia de Num artbitrary en otra. Supongamos que tengo un tipo de datos para números imaginarios; aún son números, pero realmente no puedes convertirlos correctamente en solo un Int .

En segundo lugar, ¿qué tipo de firma preferiría?

(+) :: (Num a, Num b) => a -> b -> a (+) :: (Num a, Num b) => a -> b -> b (+) :: (Num a, Num b, Num c) => a -> b -> c

Después de considerar las otras opciones, te das cuenta de que a -> a -> a es la opción más sencilla. Los resultados polimórficos (como en la tercera sugerencia anterior) son geniales, pero a veces pueden ser demasiado genéricos para ser usados ​​convenientemente.

En tercer lugar, Haskell no es Blub . La mayoría, aunque posiblemente no todas, las decisiones de diseño sobre Haskell no tienen en cuenta las convenciones y expectativas de los idiomas populares. Con frecuencia me gusta decir que el primer paso para aprender Haskell es desaprender primero todo lo que crees que sabes sobre programación. Estoy seguro de que la mayoría, si no todos, los Haskellers experimentados han sido engañados por la clase numérica Num, y varias otras curiosidades de Haskell, porque la mayoría han aprendido primero un lenguaje más "convencional". Pero ten paciencia, eventualmente llegarás a Haskell nirvana. :)


Puedes usar GHCI para aprender un poco más sobre esto. Usa el comando :t para obtener el tipo de expresión.

Prelude> :t 1 1 :: Num a => a

Entonces 1 es una constante que puede ser de cualquier tipo numérico ( Double , Integer , etc.)

Prelude> let a = 1 Prelude> :t a a :: Integer

Entonces, en este caso, Haskell dedujo que el tipo concreto para a es Integer . De manera similar, si escribe let b = 2.0 , Haskell infiere el tipo Double . El uso de let made Haskell infería un tipo más específico del que (tal vez) era necesario, y eso lleva a su problema. (Alguien con más experiencia que yo quizás pueda comentar por qué este es el caso). Como (+) tiene el tipo Num a => a -> a -> a , los dos argumentos deben tener el mismo tipo.

Puedes arreglar esto con la función fromIntegral :

Prelude> :t fromIntegral fromIntegral :: (Num b, Integral a) => a -> b

Esta función convierte los tipos de enteros a otros tipos numéricos. Por ejemplo:

Prelude> let a = 1 Prelude> let b = 2.0 Prelude> (fromIntegral a) + b 3.0