tipos - Haskell, multiplicando Int y Float dentro de una función
multiplicar haskell (4)
Intente lo siguiente en su lugar:
test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)
¿Por qué funciona esto?
- Como
b
yc
son ambosInt
s, la expresión(b - c)
también es unInt
. - La firma de tipo de
(*)
esNum a => a -> a -> a
. - Debido a que
a
es de tipoFloat
Haskell actualiza la firma de tipo de(a*)
aFloat -> Float
. - Sin embargo, dado que
(b - c)
es unInt
y no unFloat
Haskell se quejaría si intentara hacera * (b - c)
. - La firma de tipo de
fromIntegral
es(Integral a, Num b) => a -> b
. - Por lo tanto, la firma de tipo de
fromIntegral (b - c)
esNum b => b
. - Ya que
Float
es una instancia de TypeclassNum
se le permite hacera * fromIntegral (b - c)
porque la firma de tipo de(*)
esNum a => a -> a -> a
. - El resultado, a partir de la firma de tipo de
test
evalúa comoFloat
.
Espero que esto haya ayudado.
¿Por qué es que en ghci puedo entrar?
5.0 * (3 - 1)
> 10.0
Pero si intento crear una función en un archivo .hs y la cargo en:
test :: Float -> Int -> Int -> Float
test a b c = a * (b - c)
Estoy golpeado con un error? "¿No podría coincidir el tipo esperado ''Flotante'' con el tipo inferido ''Int''? ¿Y cómo puedo escribir una función que tome un punto flotante y 2 argumentos enteros y realice la operación anterior en ellos?
Estoy usando ghci v6.12.1 si eso hace una diferencia ...
Los literales numéricos (es decir, solo escribir un número en el código de Haskell) no son un tipo fijo. Son polimórficos. Deben ser evaluados en algún contexto que requiere que tengan un tipo concreto.
Así que la expresión 5.0 * (3 - 1)
no está multiplicando un Int
por un Float
. 5.0
tiene que ser algún tipo Fractional
, 3
y 1
son cada uno tipo Num
. 3 - 1
significa que tanto el 3 como el 1 tienen que ser del mismo tipo Num
, pero aún no tenemos (aún) más restricciones acerca de cuál particular es; El resultado de la resta es el mismo tipo.
El *
significa que ambos argumentos deben ser del mismo tipo, y el resultado también será el mismo tipo. Como 5.0
es un tipo Fractional
, el (3 - 1)
debe serlo. Ya sabíamos que 3
, 1
y (3 - 1)
tenían que ser algún tipo Num
pero todos los tipos Fractional
también son tipos Num
, por lo que estos requisitos no están en conflicto.
El resultado final es que toda la expresión 5.0 * (3 - 1)
es un tipo que es Fractional
, y que el 5.0
, 3
y 1
son todos del mismo tipo. Puede usar el comando :t
en GHCi para ver esto:
Prelude> :t 5.0 * (3 - 1)
5.0 * (3 - 1) :: Fractional a => a
Pero para evaluar realmente esa expresión, necesitamos hacerlo para algún tipo concreto. Si estuviéramos evaluando esto y pasándolo a alguna función que requiera Float
, Double
, o algún otro tipo Fractional
particular, Haskell lo escogería. Si solo evaluamos la expresión sin ningún otro contexto que requiera que sea un tipo particular, Haskell tiene algunas reglas predeterminadas para elegir automáticamente una (si las reglas predeterminadas no se aplican, en su lugar le dará un error de tipo sobre las variables de tipo ambiguo ).
Prelude> 5.0 * (3 - 1)
10.0
Prelude> :t it
it :: Double
Anteriormente, he evaluado 5.0 * (3 - 1)
, luego le pregunté por el tipo de variable mágica que GHCi siempre vincula al último valor que evaluó. Esto me dice que GHCi ha predeterminado mi tipo Fractional a => a
a Double
solo para calcular que el valor de la expresión era 10.0
. Al hacer esa evaluación, solo multiplicó (y restó) el Double
s, nunca multiplicó un Double
por un Int
.
Ahora, eso es lo que sucede cuando intentas múltiples literales numéricos que parecen ser de diferentes tipos. Pero su función de test
no es multiplicar los literales, es multiplicar variables de tipos conocidos particulares. En Haskell no puede multiplicar un Int
por un Float
porque el operador *
tiene el tipo Num a => a -> a -> a
- toma dos valores del mismo tipo numérico y le da un resultado que es ese tipo. Puede multiplicar un Int
por un Int
para obtener un Int
, o un Float
por un Float
para obtener un Float
. No puedes multiplicar un Int
por un Float
para obtener un ???
.
Otros idiomas admiten este tipo de operación solo mediante la inserción implícita de llamadas a funciones de conversión bajo ciertas circunstancias. Haskell nunca convierte implícitamente entre tipos, pero tiene las funciones de conversión. Solo necesita llamarlos explícitamente si desea que se los llame. Esto haría el truco:
test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)
Su función de test
fue más general, antes de agregar una firma:
> let test a b c = a * (b - c)
> :t test
test :: Num a => a -> a -> a -> a
Podrías restringirlo, pero todos los tipos deben ser iguales:
test :: Fractional a => a -> a -> a -> a -- some real types
test :: Integral a => a -> a -> a -> a -- all integer types
test :: Float -> Float -> Float -> Float
test :: Int -> Int -> Int -> Int
test :: Int -> Float -> Float -> Float --wrong
Por cierto, 2
no es Int
y 0.2
no es Float
, vamos a preguntar gchi
:
> :t 2
2 :: Num a => a
> :t 0.2
0.2 :: Fractional a => a
fromIntegral
utilizar fromIntegral
en los enteros antes de multiplicar por los flotantes.
http://www.haskell.org/haskellwiki/Converting_numbers
En GHCI, no se asume que los números sean flotantes o ints hasta que los uses. (por ejemplo: en tiempo de ejecución). Eso funciona mejor para el desarrollo de estilo REPL.
En el compilador propiamente dicho, no hay ninguna coacción automática. Se ve que la multiplicación supone que los dos valores deben pertenecer a una clase de tipos que admita la multiplicación. Por ejemplo: multiplicar entradas, o multiplicar flotantes. Como no usó ninguna otra función explícitamente escrita, asumió ints. Esa suposición luego difiere con su firma de tipo (opcional).