In the previous post, we introduce Haskell types, now let’s take a closer look at some common types Haskell provides us out of the box.
Read the other posts of this serie:
- Part 1
- Part 2 - Lambda functions
- Part 3 - First-class functions
- Part 4 - Lists
- Part 5 - Rules for recursion
- Part 6 - Pattern matching
- Part 7 - Introducing types
Before we jump in, just remember this first:
- In Haskell, we use
->to separate arguments and return values.
- The last type (from left to right) is the return value type.
- All types in Haskell starts with a capital letter to distinguish them from functions which start with lowercase letter or
In Haskell, the
Int type is the more common 32 or 64 bit integer, it is guaranteed to be at least 30 bits.
x :: Int x = 2
Integer type in Haskell is an arbitrary precision type, meaning that it can hold any integer number no matter how big, the limit is the limit of your machine’s memory.
x :: Integer x = 2
No mystery here, just pay attention that strings in Haskell must use double quotes…
name :: String name = "Lucas"
Char must use single quotes.
letter :: Char letter = 'x'
price :: Double price = 10.25
Bool type, whatever if it’s true or false, it must start with a uppercase letter, so it’s True and False.
isRaining :: Bool isRaining = False
As we saw in the Lists post, all the elements inside a list must be the same type.
scores :: [Int] scores = [8, 6, 11] prices :: [Double] prices = [2.10, 1.05, 0.89] letters :: [Char] letters = ['a', 'b', 'c']
A list of characters is the same as a string:
letters == "abc" True
To make things easier, Haskell allows us to use
String as a type synonym for
[Char], both of the types signatures below means exactly the same thing.
animal = [Char] animal = "cat" anotherAnimal :: String anotherAnimal = "lion"
The two main differences between
Lists is that a Tuple can hold different types and each Tuple is of a specific length.
streetAddress :: (String, Int) streetAddress = ("Alexanderplatz", 100) dayMonthYear :: (Int, Int, Int) dayMonthYear = (04, 03, 2018)
As we saw in the last post, we use -> to separate arguments and return values. The last type from left to right is always the return value type.
double :: Int -> Int double n = n * 2 half :: Int -> Double half n = (fromIntegral n) / 2
Functions with multiple arguments should have their type signature as follow.
makeAddress :: String -> Int -> String -> (String, Int, String) makeAddress street number town = (street, number, town)
Is important to note though, behind the scenes in Haskell, all functions take only one argument. We can rewrite the
makeAddress function by using lambda functions, so we can see a multi-argument function the way Haskell does:
makeAddress :: String -> Int -> String -> (String, Int, String) makeAddress = (\street -> (\number -> (\town -> (street, number, town))))
First-class functions types
Functions can take functions as arguments and return functions as values as well, in that case, to write type signatures, you write the individual function values in parenthesis, like the
ifEven function below.
ifEven :: (Int -> Int) -> Int -> Int ifEven f n = if even n then f n else n
Great, so all those types are neat, but what if we want a function to return any type we need, do we need to create a function for each type, like so:?
simpleInt :: Int -> Int simpleInt n = 2 simpleChar :: Char -> Char simpleChar x = x -- other types...
Yes, you can do that, but a better way is to use
type variables, which allows us to use any type we want, for instance:
simple :: a -> a simple x = x
simple function above can return any value we pass to it, doesn’t matter its type. Note that lowercase
a as type signature, any lowercase letter in a type signature indicates that any type can be used in that place.
Type variables are literally variable for types and it works exactly like a regular variable, but instead of representing a value, they represent a type.
And that’s all for today! :)