Relearn You a Haskell (Part 2: List Comprehensions, Tuples, and Types)
Tags: haskell, beginners, tutorial, refresher
Published: 2019-01-23 22:53:24

This is a continuation of my series of quick blog posts about Haskell. It's meant as a refresher for Haskell syntax and features for someone who maybe learned a bit of Haskell a while ago but who hasn't used it much and has forgotten most of what they learned. It's basically a quick summary of [Learn You a Haskell](http://learnyouahaskell.com), which is a super awesome book that you should definitely go spend money on.

_(Note: There are lots more resources available that aren't LYAH. For instance, check out [this paper](https://www.haskell.org/tutorial/haskell-98-tutorial.pdf) written by three of the designers of Haskell in 1999.)_

# list comprehensions

List comprehensions have an _output function_, one or more _input sets_, and one or more _predicates_, in that order. A basic list comprehension looks like:

```haskell
ghci> [ <output function> | <input set>, ..., <predicate>, ... ]
```

The input set is a list of values which are fed, in order, to the output function. Ultimately, the generated (output) list will consist of all of the values of the input set, which, once fed through the output function, satisfy the predicate. For example:

```haskell
ghci> [ x*x | x <- [1..10], mod x 2 == 0 ]
[4,16,36,64,100]
```

The above prints the square of all values `x`, where `x` is drawn from the set `[1..10]`, provided that `mod x 2` is equal to `0`. Another way of looking at it is that we first take the list of all numbers `[1..10]` and filter them through the predicate (`mod x 2 == 0` means we only take the even numbers `2, 4, 6, 8, 10`) and then square those numbers (so we end up with `4, 16, 36, 64, 100`).

A list comprehension with multiple input sets will loop over every possible pair (or triple, or 4-tuple, ...) from the given sets and a comprehension with multiple predicates will only return values which satisfy _all_ of the predicates. For instance:

```haskell
ghci> [ (x,y) | x <- [1..3], y <- [4..6] ]
[(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]
```

```haskell
ghci> take 10 [ x | x <- [1..], x > 10, x /= 21, odd x ]
[11,13,15,17,19,23,25,27,29,31]
```

Here are some fun, but simple(-ish), list comprehensions:

### FizzBuzz

A classic programming interview question.

```haskell
ghci> [ if (x `mod` 15 == 0) then "FizzBuzz" else if (x `mod` 3 == 0) then "Fizz" else if (x `mod` 5 == 0) then "Buzz" else show x | x <- [1..100] ]
["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz","16","17","Fizz","19","Buzz","Fizz","22","23","Fizz","Buzz","26","Fizz","28","29","FizzBuzz","31","32","Fizz","34","Buzz","Fizz","37","38","Fizz","Buzz","41",...
```

### Sieve of Eratosthenes

This list comprehension generates prime numbers.

```haskell
ghci> take 10 [ round x | x <- [2..], let l = [2..(ceiling(sqrt(x)))], all (/=0) (map (mod (round x)) l) ]
[3,5,7,11,13,17,19,23,29,31]
```

### Fibonacci Numbers

Uses the golden ratio to generate the Fibonacci sequence.

```haskell
ghci> let phi = ((1.0 + sqrt 5.0) / 2.0) in take 20 [ round (phi**x / (sqrt 5.0)) | x <- [1..] ]
[1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765]
```

...remember that strings in Haskell are just lists of characters, so you can perform list comprehensions with them, too. This one lists all of the vowels in the sentence, in the order they're seen within it:

```haskell
ghci> [ x | x <- "The quick brown fox jumps over the lazy dog.", x `elem` "aeiouy" ]
"euioouoeeayo"
```

# tuples

In Haskell, lists are homogeneous -- they can only store one kind of value (`Num`, `Bool`, `Char`, etc.). If you want to store heterogeneous values, you need to use a tuple (created using parentheses):

```haskell
ghci> [ True, 4, 'd']

<interactive>:18:12: error:
    • Couldn't match expected type ‘Bool’ with actual type ‘Char’
    • In the expression: 'd'
      In the expression: [True, 4, 'd']
      In an equation for ‘it’: it = [True, 4, 'd']

ghci> (True, 4, 'd')
(True,4,'d')
```

Haskell makes no distinction -- type-wise -- between lists of varying lengths, so long as they contain the same kind of data. So a list of lists of `Num`s could have sublists of multiple lengths:

```haskell
ghci> [[], [0], [0,1], [0,1,2]]
[[],[0],[0,1],[0,1,2]]
```

This is not the case with tuples, where a pair is distinct from a triple is distinct from a 4-tuple... even if they contain the same kind of data:

```haskell
ghci> [(0,1), (0,1,2)]

<interactive>:22:9: error:
    • Couldn't match expected type ‘(a, b)’
                  with actual type ‘(Integer, Integer, Integer)’
    • In the expression: (0, 1, 2)
      In the expression: [(0, 1), (0, 1, 2)]
      In an equation for ‘it’: it = [(0, 1), (0, 1, 2)]
    • Relevant bindings include
        it :: [(a, b)] (bound at <interactive>:22:1)
```

Get the first element of a pair (a 2-tuple) with `fst`, the second element with `snd`:

```haskell
ghci> fst ('a', 2)
'a'

ghci> snd ('a', 2)
2
```

Zip two lists element-by-element into pairs with `zip`. Note that the longer list (including infinite lists) is always truncated to the length of the shorter one:

```haskell
ghci> zip [1..] "hello"
[(1,'h'),(2,'e'),(3,'l'),(4,'l'),(5,'o')]
```

# types and classes

You can check out the type of an object or method in `ghci` with the `:t` command:

```haskell
ghci> :t (3<5) -- evaluates to True, a Bool
(3<5) :: Bool

ghci> :t "hello" -- a list of Chars ([Char]) is a String
"hello" :: [Char]

ghci> :t max -- a function which takes two Ord-erable objects of a type 'a' and returns a third
max :: Ord a => a -> a -> a
```

Here, `a` is a generic type, like `T` in Java. The bit of the type signature before the `=>` is a constraint, and in this case it says that the type `a` must be descended from `Ord` (equivalent to `a extends Ord` in Java). If you declare a function without an explicit type signature, you can explore its inferred type signature with `:t`:

```haskell
ghci> length' xs = sum [ 1 | _ <- xs ]

ghci> length' [1,2,3,4,5]
5

ghci> :t length'
length' :: Num a => [t] -> a
```

Above, we see that my `length'` method takes `[t]` (a list of objects of type `t`) and returns `a`, which must be an object descended from the `Num`-ber class. You can, and should, explicitly declare function type signatures:

```haskell
ghci> let inc :: Integer -> Integer; inc x = x + 1

ghci> inc 3
4

ghci> :t inc
inc :: Integer -> Integer
```

Here, I defined the method `inc`, which takes an `Integer` and returns an `Integer` which has been incremented by 1.

Note that `+`, `-`, `==`, `/=`, and so on are also functions, they're just infix functions by default. To pass them to `:t` (or to any other function), surround them with parentheses:

```haskell
ghci> :t (+)
(+) :: Num a => a -> a -> a

ghci> :t (/=)
(/=) :: Eq a => a -> a -> Bool
```

## built-in types

Haskell [has a few predefined types](http://www2.informatik.uni-freiburg.de/~thiemann/haskell/haskell98-report-html/basic.html), and I've already mentioned a bunch of them:

```haskell
ghci> :t False -- False and True are Bool-ean types
False :: Bool

ghci> :t "hi" -- lists of Chars are synonymous with Strings
"hi" :: [Char]

ghci> x :: String; x = "hi" -- explicit type declaration

ghci> :t x
x :: String

ghci> :t 'h' -- note: Char, not [Char]
'h' :: Char

ghci> :t [1,2] -- lists are homogeneous
[1,2] :: Num a => [a]

ghci> :t ['a','b'] -- so they have type constraints
['a','b'] :: [Char]

ghci> :t [1.1,2.2]
[1.1,2.2] :: Fractional a => [a]

ghci> :t (1,'a') -- tuples have per-member type constraints
(1,'a') :: Num a => (a, Char)

ghci> :t () -- the empty tuple is a special type, the unit datatype, ()
() :: ()

ghci> :t Nothing -- Nothing and Just are of type Maybe
Nothing :: Maybe a

ghci> :t Left -- Left and Right are of type Either
Left :: a -> Either a b

ghci> :t LT -- LT, EQ, and GT are of type Ordering
LT :: Ordering
```

...and so on. `Double`, `Float`, `Int`, `Integer`, and other predefined types also exist in Haskell, but -- as type inference gives the variable the widest possible scope (usually `Num` or `Fractional` for numbers) -- you have to explicitly declare a variable as one of these narrower types:

```haskell
ghci> x :: Double; x = 3.14

ghci> :t x
x :: Double

ghci> x :: Float; x = 3.14

ghci> :t x
x :: Float

ghci> x :: Integer; x = 42

ghci> :t x
x :: Integer

ghci> x :: Int; x = 42

ghci> :t x
x :: Int
```

What's the difference between `Int` and `Integer`, though? `Int` is bounded (and fast), but `Integer` is not (and slow):

```haskell
ghci> factorial :: Int -> Int; factorial n = product [1..n]  

ghci> factorial 10 -- okay, this is fine
3628800

ghci> factorial 100 -- (silent) overflow!
0

ghci> factorial :: Integer -> Integer; factorial n = product [1..n]

ghci> factorial 100 -- woo! works great!
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

ghci> factorial 10000 -- also works fine! so many digits...
2846259680917054518906413212119868890148051401702799230794179994274411340003764443772990786757784775815884062142317528830042339940153518739052421161382716174819824199827592418289259787898124253120594659962598670656016157203603239792632873671705574197596209947972034615369811989709261127750048419884541047554464244213657330307670362882580354896746111709736957860367019107151273058728104115864056128116538532596842582599558468814643042558983664931705925171720427659740744613340005419405246230343686915405940406622782824837151203832217864462718382292389963899282722187970245938769380309462733229257055545969002787528224254434802112755901916942542902891690721909708369053987374745248337289952180236328274121704026808676921045155584056717255537201585213282903427998981844931361064038148930449962159999935967089298019033699848440466541923625842494716317896119204123310826865107135451684554093603300960721034694437798234943078062606942230268188522759205702923084312618849760656074258627944882715595683153344053442544664841689458042570946167361318760523498228632645292152942347987060334429073715868849917893258069148316885425195600617237263632397442078692464295601230628872012265295296409150830133663098273380635397290150658182257429547589439976511386554120812578868370423920876448476156900126488927159070630640966162803878404448519164379080718611237062213341541506599184387596102392671327654698616365770662643863802984805195276953619525924093090861447190739076858575593478698172073437209310482547562856777769408156407496227525499338411280928963751699021987049240561753178634693979802461973707904186832993101655415074230839317...
```

To me (someone with a mainly C/C++/Java background) that is pretty neat.

## built-in classes

Haskell classes (also called typeclasses) are sort of like Java interfaces in that any child class derived from a particular parent class is guaranteed to implement some specific behaviour.

Some of the more common ones include:

### [`Eq`](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Eq) and [`Ord`](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Ord)

Classes which implement `Eq` can be tested for equality. All predefined classes (except those related to I/O) implement `Eq`.

Similarly, classes which implement `Ord` can be ordered using `<`, `>`, and so on. All numeric types, as well as `Char`s and lists, extend the `Ord` class.

### [`Show`](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Show)

Classes which implement `Show` can be represented as `String`s. A variable of any `Show`-implementing type can be converted to a `String` with the `show` method:

```haskell
ghci> show True
"True"

ghci> show 1.1
"1.1"
```

### [`Read`](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Read)

`Read` can be thought of as the opposite of `Show`. The `Read` class parses `String`s as variables of the appropriate type, where "the appropriate type" is determined by the way in which the variable is `read`:

```haskell
ghci> read "4.8" + 2.0
6.8

ghci> read "[1,2,3]" ++ [4,5,6]
[1,2,3,4,5,6]
```

`read`-ing a variable and doing nothing with it will throw an error, because Haskell doesn't know what kind of type to give it:

```haskell
ghci> read "4.8"
*** Exception: Prelude.read: no parse
```

You can get around this with an explicit type annotation:

```haskell
ghci> read "4.8" :: Double
4.8
```

### [`Bounded`](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Bounded)

`Bounded` types have maximum and minimum limits. You can see what these are with `minBound` and `maxBound`:

```haskell
ghci> minBound :: Int
-9223372036854775808

ghci> maxBound :: Int
9223372036854775807

ghci> minBound ::Bool
False

ghci> maxBound :: Bool
True
```

### [`Num`](http://hackage.haskell.org/package/base-4.12.0.0/docs/Prelude.html#t:Num)

`Num` is the basic numeric class in Haskell. Any class which extends `Num` must implement `+`, `*`, `abs`, `signum`, negation, and a few other things. `Real` and `Fractional` both derive from `Num`. (And `Real` also from `Ord`.)

`Fractional` is implemented by the predefined, non-integral numeric classes `Float` and `Double`, while `Int` and `Integer` implement the `Integral` class which itself implements the `Real` class.

A class hierarchy outlining all of this [can be found in the Haskell 98 report](https://www.haskell.org/onlinereport/basic.html).

---

As always, [Learn You a Haskell](http://learnyouahaskell.com/types-and-typeclasses#believe-the-type) has a great explanation of types and classes, and goes into more detail than I have here. I strongly recommend it.

I hope this post has jogged your memory a bit about working with list comprehensions, tuples, and types in Haskell. At this point, you should know enough to go out and complete some coding challenges! It's a great language for one-liners!

_Coming up in Part 3: functions_