Haskell, multiplying Int and Float within a function

22,575

Solution 1

Numeric literals (i.e. just typing a number in Haskell code) are not some fixed type. They are polymorphic. They need to be evaluated in some context that requires them to have a concrete type.

So the expression 5.0 * (3 - 1) is not multiplying an Int by a Float. 5.0 has to be some Fractional type, 3 and 1 are each some Num type. 3 - 1 means that the 3 and the 1 both have to be the same Num type, but we still don't (yet) have any more constraints about which particular one it is; the result of the subtraction is the same type.

The * means both arguments have to be the same type, and the result will be the same type too. Since 5.0 is some Fractional type, the (3 - 1) must be too. We already knew that 3, 1, and (3 - 1) had to be some Num type but all Fractional types are also Num types, so this requirements are not in conflict.

The end result is that the whole expression 5.0 * (3 - 1) is some type that is Fractional, and the 5.0, 3, and 1 are all the same type. You can use the :t command in GHCi to see this:

Prelude> :t 5.0 * (3 - 1)
5.0 * (3 - 1) :: Fractional a => a

But to actually evaluate that expression, we need to do so for some concrete type. If we were evaluating this and passing it to some function that required Float, Double, or some other particular Fractional type then Haskell would pick that one. If we just evaluate the expression with no other context requiring it to be a particular type, Haskell has some defaulting rules to automatically choose one for you (if the defaulting rules don't apply it will instead give you a type error about ambiguous type variables).

Prelude> 5.0 * (3 - 1)
10.0
Prelude> :t it
it :: Double

Above I've evaluated 5.0 * (3 - 1), then asked for the type of the magic it variable which GHCi always binds to the last value it evaluated. This tells me that GHCi has defaulted my Fractional a => a type to just Double, in order to compute that the value of the expression was 10.0. In doing that evaluation, it only ever multipled (and subtracted) Doubles, it never multiplied a Double by an Int.


Now, that's what's going on when you attempt to multiple numeric literals that look like they might be of different types. But your test function isn't multiplying literals, it's multiplying variables of particular known types. In Haskell you can't multiply an Int by a Float because the * operator has type Num a => a -> a -> a - it takes two values of the same numeric type and gives you a result that is that type. You can multiply an Int by an Int to get an Int, or a Float by a Float to get a Float. You can't multiply an Int by a Float to get a ???.

Other languages support this sort of operation only by implicitly inserting calls to conversion functions under some circumstances. Haskell never implicitly converts between types, but it has the conversion functions. You just need to call them explicitly if you want them to be called. This would do the trick:

test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)

Solution 2

Try the following instead:

test :: Float -> Int -> Int -> Float
test a b c = a * fromIntegral (b - c)

Why does this work?

  1. Since b and c are both Ints the expression (b - c) is also an Int.
  2. The type signature of (*) is Num a => a -> a -> a.
  3. Because a is of type Float Haskell updates the type signature of (a*) to Float -> Float.
  4. However since (b - c) is an Int and not a Float Haskell would complain if you tried doing a * (b - c).
  5. The type signature of fromIntegral is (Integral a, Num b) => a -> b.
  6. Hence the type signature of fromIntegral (b - c) is Num b => b.
  7. Since Float is an instance of typeclass Num you're allowed to do a * fromIntegral (b - c) because the type signature of (*) is Num a => a -> a -> a.
  8. The result, from the type signature of test evaluates to Float.

Hope this helped.

Solution 3

You need to use fromIntegral on the integers before multiplying by the floats.

http://www.haskell.org/haskellwiki/Converting_numbers

In GHCI, the numbers aren't assumed to be floats or ints until you use them. (e.g: at run time). That works out better for REPL-style development.

In the compiler proper, there isn't any automatic coercion. It it sees the multiplication assumes that the two values must belong to a type-class that supports multiplication. e.g: multiplying ints , or multiplying floats. As you didn't use any other explicitly typed functions, it assumed ints. That assumption then differs with your (optional) type signature.

Solution 4

Your test function was more general, before you add a signature:

> let test a b c = a * (b - c)
> :t test
  test :: Num a => a -> a -> a -> a

You could restrict it, but all types must be the same:

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

By the way, 2 isn't Int and 0.2 isn't Float, let ask gchi:

> :t 2
 2 :: Num a => a
> :t 0.2
 0.2 :: Fractional a => a
Share:
22,575
Sam Coulter
Author by

Sam Coulter

Updated on September 26, 2020

Comments

  • Sam Coulter
    Sam Coulter over 3 years

    Why is it that in ghci I can enter:

    5.0 * (3 - 1)
    > 10.0
    

    But if I try and create a function in a .hs file and load it in:

    test :: Float -> Int -> Int -> Float
    test a b c = a * (b - c)
    

    I am hit with an error? "Couldnt match expected type 'Float' against inferred type 'Int'? And how can I write a function that takes in one floating point and 2 integer arguments and performs the above operation on them?

    I am using ghci v6.12.1 if that makes a difference...