Skip to content

Explicit Conversion

C and C++ generally allow you to mix and match integers and floating point numbers at will in arithmetic expressions. The operands in an expression are coerced—that is, implicitly converted—to the least general type that can represent all the operand types in the expression. In Haskell, the primary purpose of the type system is to check that there are no logical errors in your code.1 If you use an integer in a context where a floating point number is expected or vice versa, the compiler worries that this is a logical error in your code. So, instead of silently coercing from one number type to the other, it gives you a type error. If you do want to use an integer value in a floating point expression, something that is not uncommon, you must explicitly say so, by converting from integer to floating point number or the other way.

Similarly, you cannot use an Integer where an Int is expected or vice versa. You need to be explicit about the conversion.

To convert from an integral type (Int or Integer) to any other number type, use fromIntegral:

GHCi
>>> fromIntegral 3 :: Double
3.0

You can tell that the conversion happened by the fact that the result is printed with a decimal point. Since I did not use the result of the conversion in some context from which the compiler could deduce the type to which I want to convert 3, I had to specify the type I wanted by stating that I want the result of fromIntegral 3 to be of type Double.

To convert between Float and Double, you have float2Double and double2Float. To use them, you need to import the GHC.Float module. In GHCi, you can either use the standard Haskell command import, which we will discuss in detail later, or you can use the GHCi command :module or :m.

GHCi
>>> import GHC.Float
>>> float2Double 3.0
3.0

or

GHCi
>>> :m GHC.Float
>>> float2Double 3.0
3.0

Hmm, did anything happen here? We can't tell. There are two useful little commands built into GHCi that allow us to gather information about different values. First, there is :type or :t for short. It tells us the type of a value:

GHCi
>>> :t float2Double 3.0
float2Double 3.0 :: Double
>>> :t double2Float 3.0
double2Float 3.0 :: Float

This tells us that the result of float2Double 3.0 indeed has type Double, and the result of double2Float 3.0 has type Float, as expected.

The other useful command is :info or :i. It provides basic information about a type, value or function. For a value or function—remember, in Haskell, functions are just special types of values—this information includes the type and where it is defined:

GHCi
>>> :info (+)
class Num a where
  (+) :: a -> a -> a
  ...
    -- Defined in ‘GHC.Num’
infixl 6 +

This tells us that the addition operator (+) is defined in the GHC.Num module where it is introduced as part of the Num type class, which I mentioned before. For any number type a, we can apply (+) to two arguments of type a and obtain a result that is again of type a. The last line can be interesting in some contexts. It tells you that when used in infix notation, addition is left-associative: the expression 1+2+3 is the same as (1+2)+3. The alternative is that it would be right-associative, which would be expressed with the keyword infixr. The number after infixl is the precedence level of (+). Let's have a look at (*):

GHCi
>>> :info (*)
class Num a where
  ...
  (*) :: a -> a -> a
  ...
    -- Defined in ‘GHC.Num’
infixl 7 *

Multiplication is also left-associative, but it has a higher precedence than addition. Thus, the expression 3 + 4 * 5 is parsed as 3 + (4 * 5), not (3 + 4) * 5.

To convert from any floating point type to an integral type, you have a range of different rounding operations available. truncate rounds the floating point number towards 0 and returns the resulting integer value.

GHCi
>>> truncate 3.7
3
>>> truncate (-3.7)
-3

I had to enclose the -3.7 in parentheses here because otherwise, the parser would have interpreted the expression as trying to subtract 3.7 from truncate, which is a function. Since this makes no sense, you get a rather cryptic error message from GHCi if you try.

floor rounds down.

GHCi
>>> floor 3.7
3
>>> floor (-3.7)
-4

ceiling rounds up.

GHCi
>>> ceiling 3.7
4
>>> ceiling (-3.7)
-3

round applies the type of rounding you may have learned in high school. It rounds towards the closest integer. If the floating point number is equidistant to two integers (it is a multiple of 1/2), then the even number closest to the floating point number is returned.

GHCi
>>> round 2.3
2
>>> round 2.7
3
>>> round 2.5
2
>>> round 3.5
4

Exercise

Use GHCi as a calculator. Evaluate the following arithmetic expressions:

  • 3 * (4 + 5)
  • 3 ^ 2
  • 3 ** 2
  • 4 ** 0.5
  • 4 ^ 0.5

The last one is expected not to work.

Solution
GHCi
>>> 3 * (4 + 5)
27
>>> 3 ^ 2
9
>>> 3 ** 2
9.0
>>> 4 ** 0.5
2.0
>>> 4 ^ 0.5

<interactive>:21:3: error:
    • Could not deduce (Integral b0) arising from a use of ‘^’
      from the context: Num a
        bound by the inferred type of it :: Num a => a
        at <interactive>:21:1-7
      The type variable ‘b0’ is ambiguous
      These potential instances exist:
        instance Integral Integer -- Defined in ‘GHC.Real’
        instance Integral Int -- Defined in ‘GHC.Real’
        instance Integral Word -- Defined in ‘GHC.Real’
        ...plus one instance involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the expression: 4 ^ 0.5
      In an equation for ‘it’: it = 4 ^ 0.5

<interactive>:21:5: error:
    • Could not deduce (Fractional b0) arising from the literal ‘0.5’
      from the context: Num a
        bound by the inferred type of it :: Num a => a
        at <interactive>:21:1-7
      The type variable ‘b0’ is ambiguous
      These potential instances exist:
        instance Fractional Double -- Defined in ‘GHC.Float’
        instance Fractional Float -- Defined in ‘GHC.Float’
        ...plus one instance involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the second argument of ‘(^)’, namely ‘0.5’
      In the expression: 4 ^ 0.5
      In an equation for ‘it’: it = 4 ^ 0.5

Exercise

Use Hoogle to find out about the (**) and (^) operators. Explain why the expression 4 ^ 0.5 in the previous exercise did not work.

Solution

The (**) and (^) operators both express exponentiation. Neither is more general than the other.

The type of (**) is Floating a => a -> a -> a. This means that both the base and the exponent can be floating point numbers, but they must be floating point numbers of the same type a. That's why the expression 4 ** 0.5 in the previous exercise worked just fine.

The type of (^) is (Num a, Integral b) => a -> b -> a. Thus, the base of the exponential expression can be of any number type a, and the exponent of the expression can be of a different number type b. Where (^) is more restrictive than (**) is that the exponent needs to be an integral number type. That's why 4 ^ 0.5 in the previous exercise didn't work; 0.5 is not representable as any integral number type.


  1. I would argue that this is not the primary purpose of types in C or C++. The main resaon why you specify the types of variables in C and C++ is so the compiler knows which machine-level representation of the values you want to use. If the purpose were to check for logical errors in your code, then C and C++ would be more strict about which types can be mixed and matched in expressions, and they would support less automatic coercion.