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 . 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 for each different case. Interoperating between these types can be very painful. numeric types different types 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 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 might have a different set of bounds than on a 64-bit machine. Luckily, is a member of the typeclass. So in your code, you can always query the max and min bounds to check for overflow. Int Int Int Bounded 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 your int will take. There are several different types that allow you to do exactly this. We have , , , and . They allow you to have more definite bounds on your number, while giving you the same basic functionality as . Obviously is 8 bits, is 16 bits, and so on. exactly how much memory Int Int8 Int16 Int32 Int64 Int Int8 Int16 Now, there are also circumstances where you want your integer to be . In this case, you’ll need to use the type. This type establishes no limit on how big your number can be. It does not implement the 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. unbounded Integer Bounded Word Types Once you understand the different classes, next up are the types. Each size has a corresponding type. These types work the same way, except they are . So whereas an goes from -128 to 127, a can contain values from 0 to 255. In fact, has a function to give you a list of values as a representation of the byte string. Int Word Int Word unsigned Int8 Word8 Data.ByteString Word8 So what lessons then can we take from these different integral types? How do we choose what type to use? For most cases, 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 types instead of 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 . Be aware that if the values are this large, you'll face a performance penalty. Int Word Int Integer The Integral Typeclass You’ll often come across a situation where you want to write a more 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 typeclass is for. It encapsulates a few different pieces of functionality. general function Integral First, it facilitates the conversion between the different integral types. It supplies a function with the type: toInteger toInteger :: Integral a => a -> Integer This allows you to use the unbounded 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. Integer The other functions within the typeclass deal with . For instance, there are quotient and remainder functions. These allow you to perform integral division when you don't know the exact type. Integral division on integral types 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 and functions are like and , 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 . But that’s the price of a strong typed system. div mod quot rem 5 / 2 Floating Point Numbers Of course, we also have a couple different types to represent floating point numbers. Haskell has two main floating point types: and . The type is a single-precision floating point number. And of course, is double precision. They represent the same basic concept, but allows a of values with . At the same time, it takes up twice as much memory as a . Converting between these two types is easy using the following functions: Float Double Float Double Double larger range more precision Float float2Double :: Float -> Doubledouble2Float :: Double -> Float There is a typeclass that encapsulates these types (as well as a couple more obscure versions). This is the typeclass. It allows a host of different operations to be performed on these types. A lot of these are mathematical functions. Floating For instance, one of these functions is just . So if your function takes any type in the typeclass, you can still get a reliable value for pi. also incorporates other math concepts as well, like square roots, exponents, trigonometric functions, and so on. pi Floating Floating Other Numeric Typeclasses There are a few other numeric typeclasses. They encapsulate behavior both for floating numbers and integers. For instance, we have the typeclass which allows us to convert anything to a number. There is also the typeclass. It allows us to perform certain operations that are natural on fractions. These include calculating . We can then mash these two classes together to get . 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 , , , and so on. Real Rational Fractional reciprocals and performing true (non-integer) division RealFrac ceiling round truncate 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 . Besides what we’ve covered, two types of transitions stand out the most. Gentle Introduction to Haskell First, there is . This allows you to convert from any integral type to any numeric type. It should be your go-to when you but have . Second, there is the process of going from a floating point number to an integer type. You’ll generally use one of , and , depending on your desired coercion. Finally, remember the function. It is generally the answer when going between integral types. fromIntegral need a floating point number some integer type round floor ceiling toInteger fromIntegral :: (Integral a, Num b) => a -> bround :: (Fractional a, Integral b) => a -> bfloor :: (Fractional a, Integral b) => a -> bceiling :: (Fractional a, Integral b) => a -> btoInteger :: (Integral a) => a -> Integer Scientific If you do any level of web programming, you’ll likely be encoding things in JSON using the library. We’ll go more in depth on this library in a later article (it’s super useful). But it uses the 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: Data.Aeson Scientific -- Creates 70000createScientificNum :: ScientificcreateScientificNum = scientific 7 4 -- Conversion to float (i.e. 70000.0)convertToFloat :: DoubleconvertToFloat = toBoundedRealFloat createScientificNum -- Convert to integer, might fail if the scientific is not an integerconvertToInt :: Maybe IntegerconvertToInt = toBoundedInteger createScientificNum Num Typeclass So on top of the different floating and integral typeclasses, we also have the typeclass. This brings them all together under one roof. The required elements of this typeclass are your basic math operators. Num 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 typeclass. The class let us use a string literal to represent our different types in code. In the same way, the 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. IsString IsString Num newtype MyNumberType = MyNumberType Int instance Num MyNumberType where -- Implement math functions using underlying int value ... myNumWithLiteral :: MyNumberTypemyNumWithLiteral = 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 . 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. Getting Started Checklist If you’ve experimented a little bit and want some extra practice, you should download our . It contains two chapters of materials as well as 10 practice problems! Recursion Workbook Above all, be sure to visit the blog for more awesome Haskell content, with a new article every Monday! Monday Morning Haskell is how hackers start their afternoons. We’re a part of the family. We are now and happy to opportunities. Hacker Noon @AMI accepting submissions discuss advertising & sponsorship To learn more, , , or simply, read our about page like/message us on Facebook tweet/DM @HackerNoon. If you enjoyed this story, we recommend reading our and . Until next time, don’t take the realities of the world for granted! latest tech stories trending tech stories