Dear students,

Today we solve more problems in Haskell together. Along the way we will discover some of the peculiarities of functional programming, like the lack of iteration, the supremacy of recursion, and the beauty of pattern matching:

Here are the problems we’ll solve:

- Last time we started to write a function to determine if a list contains an element. Our first attempt used simple conditionals and recursion:
contains :: [a] -> Bool contains needle haystack = if haystack == [] then False else if head haystack == needle then True else contains needle $ tail haystack

This is okay, but Haskell sports a really nice*guard*syntax that shows the decision structure that we often see in mathematics:contains needle haystack | head haystack == needle = True | otherwise = False

A little shorter, right? Well, we also have a`case`

statement in Haskell, which allows us to use pattern matching:contains needle haystack = case haystack of [] -> False (first : rest) -> first == needle || contains needle rest ] This doesn't seem better than the guards. However, Haskell allows us to rewrite this structure in a more digestable way: [code contains _ [] = False contains (first:rest) = first == needle || contains needle rest

Under the hood these seemingly multiple definitions of`contains`

get turned into a case statement. But this syntax lets us focus on one case at a time. In my opinion, it is the pinnacle of clarity. - Let’s write
`sum`

to sum up a list of numbers in a similar way:sum [] = 0 sum (first:rest) = first + sum rest

- Let’s write
`head'`

:head' [] = error "Can't decapitate the empty list!" head' (first:rest) = first

Notice that in the second case we don’t use`rest`

at all. Haskell lets us acknowledge its irrelevance by blanking out the identifier name:head' (first:_) = first

This feature is not unique to Haskell. Check out this from Ruby:The array returned bydef foo [1, 2] end first, second = foo

`foo`

is destructured into`first`

and`second`

. Suppose we don’t need`second`

:If we didn’t explicitly expressfirst, _ = foo

`_`

, no destructuring would take place, and`first`

would hold`[1, 2]`

. C++ also allows us to disregard parameters simply by not naming them in the formal parameters:Why would you ever write a function to take in a certain number of parameters and then completely ignore one? That sounds ridiculous. Except when you get into the world of inheritance, where you don’t get to choose your parameters.void foo(int a, int) { return f(a); }

- Let’s write
`tail'`

:tail' [] = error "Can't detorso the empty list!" tail' (_:rest) = rest

- Let’s write
`belows`

, which gives us back all elements of a list less than some threshold:belows [] = [] belows threshold (first:rest) | first < threshold = first : belows rest | otherwise = belows rest

- Let’s write an enum-like structure:
data Color = Red | Green | Blue | Yellow | Orange

- Let’s write
`eval`

, which evaluates an expression, just like we saw on the midterm. First, we need to create a hierarchy:data Expr = ExprInt Integer | ExprNegate Expr | ExprMultiply Expr Expr | ExprAdd Expr Expr

Now we can define`eval`

:eval :: Expr -> Integer eval (ExprInt n) = n eval (ExprNegate e) = -eval e eval (ExprMultiply e1 e2) = eval e1 * eval e2 eval (ExprAdd e1 e2) = eval e1 + eval e2

We need a crazy expression to test this on. How about`(17 + 1) * -(0 + 2))`

?eval (ExprMultiply (ExprAdd (ExprInt 17) (ExprInt(1))) (ExprNegate (ExprAdd (ExprInt 0) (ExprInt 2)))

- Let’s write
`contains`

again, but this time let’s operate on a binary search tree:data Tree a = Leaf a | Branch (Tree a) a (Tree a) has :: Ord a => a -> Tree a -> Bool has needle (Leaf value) = value == needle has needle left value right | target == needle = True | target < needle = has needle left | otherwise = has needle right

See you next time, when we discuss the various ways we can define functions in Haskell!

Sincerely,

## Comments