Haskell, multiplying Int and Float within a function

后端 未结 4 2016
故里飘歌
故里飘歌 2021-01-01 10:41

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:



        
相关标签:
4条回答
  • 2021-01-01 11:19

    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
    
    0 讨论(0)
  • 2021-01-01 11:26

    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)
    
    0 讨论(0)
  • 2021-01-01 11:44

    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.

    0 讨论(0)
  • 2021-01-01 11:44

    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.

    0 讨论(0)
提交回复
热议问题