Last week we explored the many different string types in Haskell. But this isn't the only situation where we seem to have an abundance of similar types. We can also see this in Haskell's **numeric types**. Again, we have the issue that we often want to represent numbers in slightly different ways. But Haskell's type system forces us to have **different types** for each different case. Interoperating between these types can be very painful.

This is another one of those things with Haskell that can definitely throw beginners for a loop. It can even make them say, “ugh, this language sucks, I’m going back to Javascript" (where numbers are easier). But there are a few simple rules you have to get under your belt, and then things become much better. Let's start by summarizing the different numeric types we can use.

## Int Types

The most familiar integral type is the simple `Int`

type. This represents your standard machine-varying, bounded, signed integer. According to the documentation, it is guaranteed to have a range of at least -2^29 to 2^29. So if you’re on a 32-bit machine, your `Int`

might have a different set of bounds than on a 64-bit machine. Luckily, `Int`

is a member of the `Bounded`

typeclass. So in your code, you can always query the max and min bounds to check for overflow.

```
class Bounded a where
minBound :: a
maxBound :: a
```

Now, suppose you don’t want to be at the whims of the machine for how large a value you can store. Sometimes, you want to know **exactly how much memory** your int will take. There are several different `Int`

types that allow you to do exactly this. We have `Int8`

, `Int16`

, `Int32`

, and `Int64`

. They allow you to have more definite bounds on your number, while giving you the same basic functionality as `Int`

. Obviously `Int8`

is 8 bits, `Int16`

is 16 bits, and so on.

Now, there are also circumstances where you want your integer to be **unbounded**. In this case, you’ll need to use the `Integer`

type. This type establishes no limit on how big your number can be. It does not implement the `Bounded`

typeclass. Naturally, this comes at a performance penalty. If your number is too large to fit in a single register in memory, Haskell will use a byte array to represent the number. Operations on this number will be slower. Hardware is designed to make mathematical operations on smaller values extremely fast. You won’t be able to get these speedups on larger numbers. But if you need the higher bounds then you don't have a choice.

## Word Types

Once you understand the different `Int`

classes, next up are the `Word`

types. Each `Int`

size has a corresponding `Word`

type. These types work the same way, except they are **unsigned**. So whereas an `Int8`

goes from -128 to 127, a `Word8`

can contain values from 0 to 255. In fact, `Data.ByteString`

has a function to give you a list of `Word8`

values as a representation of the byte string.

So what lessons then can we take from these different integral types? How do we choose what type to use? For most cases, `Int`

is probably fine. Depending on the domain, you may be able to limit the number of bytes that you need to use. This is where the various sizes of integers come into play. If you know your value will be positive and not negative, definitely use `Word`

types instead of `Int`

types. This will give you more wiggle room against those bounds. If you're dealing with values that might exceed 64-bit bounds, you have no choice but to use `Integer`

. Be aware that if the values are this large, you'll face a performance penalty.

## The Integral Typeclass

You'll often come across a situation where you want to write a more **general function** on numbers. You don't know exactly what type you'll be dealing with. But you know it'll be one of these integer types. Like a good Haskell developer, you want your code to be as polymorphic as possible. This is what the `Integral`

typeclass is for. It encapsulates a few different pieces of functionality.

First, it facilitates the conversion between the different integral types. It supplies a `toInteger`

function with the type:

`toInteger :: Integral a => a -> Integer`

This allows you to use the unbounded `Integer`

type as a go-between amongst your integral types. It allows you to write reasonably polymorphic code. There can also be drawbacks though. By using the lowest common denominator, you might make your program's performance worse. So for performance critical code, you might need to reason more methodically about which type you should use.

The other functions within the `Integral`

typeclass deal with **division on integral types**. For instance, there are quotient and remainder functions. These allow you to perform integral division when you don't know the exact type.

```
class Integral a where
toInteger :: a -> Integer
quot :: a -> a -> a
-- ^^ Integer division, e.g. 9 `quot` 2 = 4
rem :: a -> a -> a
-- ^^ Remainder, e.g. 9 `rem` 2 = 1
div :: a -> a -> a
mod :: a -> a -> a
```

As a note, the `div`

and `mod`

functions are like `quot`

and `rem`

, but round towards negative infinity instead of 0. It can be a little constricting to have to jump through these hoops. This is especially the case when your javascript friends can just get away with always writing `5 / 2`

. But that’s the price of a strong typed system.

## Floating Point Numbers

Of course, we also have a couple different types to represent floating point numbers. Haskell has two main floating point types: `Float`

and `Double`

. The `Float`

type is a single-precision floating point number. And of course, `Double`

is double precision. They represent the same basic concept, but `Double`

allows a **larger range** of values with **more precision**. At the same time, it takes up twice as much memory as a `Float`

. Converting between these two types is easy using the following functions:

```
float2Double :: Float -> Double
double2Float :: Double -> Float
```

There is a typeclass that encapsulates these types (as well as a couple more obscure versions). This is the `Floating`

typeclass. It allows a host of different operations to be performed on these types. A lot of these are mathematical functions.

For instance, one of these functions is just `pi`

. So if your function takes any type in the `Floating`

typeclass, you can still get a reliable value for pi. `Floating`

also incorporates other math concepts as well, like square roots, exponents, trigonometric functions, and so on.

## Other Numeric Typeclasses

There are a few other numeric typeclasses. They encapsulate behavior both for floating numbers and integers. For instance, we have the `Real`

typeclass which allows us to convert anything to a `Rational`

number. There is also the `Fractional`

typeclass. It allows us to perform certain operations that are natural on fractions. These include calculating **reciprocals and performing true (non-integer) division**. We can then mash these two classes together to get `RealFrac`

. This typeclass allows us to express a number as a true fraction (so a tuple of two integral numbers). It has several other useful functions like `ceiling`

, `round`

, `truncate`

, and so on.

## Conversion Mania

We’ve gone over some of the conversions between similar types. But it’s difficult to keep track of all the different ways to convert between values. For a more exhaustive list, check out Gentle Introduction to Haskell. Besides what we've covered, two types of transitions stand out the most.

First, there is `fromIntegral`

. This allows you to convert from any integral type to any numeric type. It should be your go-to when you **need a floating point number** but have **some integer type**. Second, there is the process of going from a floating point number to an integer type. You’ll generally use one of `round`

, `floor`

and `ceiling`

, depending on your desired coercion. Finally, remember the `toInteger`

function. It is generally the answer when going between integral types.

```
fromIntegral :: (Integral a, Num b) => a -> b
round :: (Fractional a, Integral b) => a -> b
floor :: (Fractional a, Integral b) => a -> b
ceiling :: (Fractional a, Integral b) => a -> b
toInteger :: (Integral a) => a -> Integer
```

## Scientific

If you do any level of web programming, you’ll likely be encoding things in JSON using the `Data.Aeson`

library. We’ll go more in depth on this library in a later article (it’s super useful). But it uses the `Scientific`

type, which represents numbers in “scientific” notation. In this format, you have a base multiplied by 10 to a certain power. You won’t encounter this type too often, but it’s useful to know how to construct it. In particular you’ll often want to take simple integer or floating point values and convert them back and forth. Here are some code examples how:

```
-- Creates 70000
createScientificNum :: Scientific
createScientificNum = scientific 7 4
-- Conversion to float (i.e. 70000.0)
convertToFloat :: Double
convertToFloat = toBoundedRealFloat createScientificNum
-- Convert to integer, might fail if the scientific is not an integer
convertToInt :: Maybe Integer
convertToInt = toBoundedInteger createScientificNum
```

## Num Typeclass

So on top of the different floating and integral typeclasses, we also have the `Num`

typeclass. This brings them all together under one roof. The required elements of this typeclass are your basic math operators.

```
class Num a where
(+), (*), (-) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
```

This class is also somewhat analogous to the `IsString`

typeclass. The `IsString`

class let us use a string literal to represent our different types in code. In the same way, the `Num`

typeclass allows us to represent our value using a numeric literal. We don’t even need a compiler extension for it! This is particularly helpful when you make a "newtype" around a number. It's nice to not have to always write the constructor for it.

```
newtype MyNumberType = MyNumberType Int
instance Num MyNumberType where
-- Implement math functions using underlying int value
...
myNumWithLiteral :: MyNumberType
myNumWithLiteral = 5
```

## Conclusion

Numbers, like string types, can be super confusing in Haskell. It’s hard to keep them straight. It’s especially difficult when you’re trying to interoperate between different types. It feels like since numeric types are so similar it should be easy to use them. And for once the type system seems to get in the way of simplicity here.

Luckily, polymorphism allows us many different avenues to fix this. We can almost always make our functions apply to lots of different numeric types. We’ll often need to convert our types to make things interoperate. But there are generally some nice functions that allow us to do this with ease.

If you’ve never programmed in Haskell before and want to try it out, you should check out our Getting Started Checklist. It will walk you through installing Haskell on your computer. It will also point you towards some tools that will help you in learning the language.

If you’ve experimented a little bit and want some extra practice, you should download our Recursion Workbook. It contains two chapters of materials as well as 10 practice problems!