0% found this document useful (0 votes)
24 views

Functions

Uploaded by

alikurban788
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views

Functions

Uploaded by

alikurban788
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 5

add handouts

7de2c2c3
Martin Escardo authored 3 weeks ago

functions.md 11.16 KiB

Functions in Haskell
• Read also Chapter 4 of the text book "Programming in Haskell.

Overview
In this lesson, we study various ways how functions can be defined in Haskell. We will study the following ways:

 Composition of existing functions


 Conditionals ( if _ then _ else _ )
 Guarded equations
 Pattern matching
 Lambda expressions

At the end, we will also look at operators (infix function symbols such as ++ ), and how to turn them into functions.

Composing functions
A video for this section, including explanation of the exercise, is here.

We can compose existing functions to get new ones. For instance:

removeLast :: [a] -> [a]


removeLast xs = reverse (tail (reverse xs))

removeElem :: Int -> [a] -> [a]


removeElem n xs = removeLast (take n xs) ++ drop n xs

Exercise: Using the functions above, write a function that removes both the first and the last element of a list.

Conditionals
Haskell provides if _ then _ else _ . It is typed Bool -> a -> a -> a , polymorphically.

abs' :: Integer -> Integer


abs' n = if n >= 0 then n else -n

Note: The else branch is mandatory.

We can nest if _ then _ else _ :

howMuchDoYouLikeHaskell :: Int -> String


howMuchDoYouLikeHaskell x = if x < 3 then "I dislike it!" else
if x < 7 then "It's ok!" else
"It's fun!"

This is difficult to read, however; guarded equations (see below) can be more pleasant to read. We will avoid conditionals.

Exercise: Read the discussion about if _ then _ else _ on the Haskell wiki.

Guarded equations
A video for this section, including explanation for the exercise, is here.

Guarded equations are an alternative to if _ then _ else _ expressions. They are often more readable:

abs :: Int -> Int


abs n | n >= 0 = n
| otherwise = -n

Here, n >= 0 and otherwise are called guards; they are Booleans. The function returns the value after the first guard that evaluates to True .
Guarded equations are more convenient to use than if _ then _ else _ :

howMuchDoYouLikeHaskell2 :: Int -> String


howMuchDoYouLikeHaskell2 x | x < 3 = "I dislike it!"
| x < 7 = "It's ok!"
| otherwise = "It's fun!"

Exercise: Using guarded equations, write a function of type Int -> Int -> Bool that returns True if the first argument is greater than the second and
less than twice the second.

Pattern matching
Pattern matching analyses the input according to how it is built. The input is matched against a sequence of patterns; the first pattern that matches
determines the output of the function.

Overview
There are only two possibilities for what a Boolean value can be: True or False . It is hence sufficient to have patterns for these two cases:

notB :: Bool -> Bool


notB False = True
notB True = False

There is only one way to make a pair:

swap :: (a, b) -> (b, a)


swap (x,y) = (y,x)

There are two ways to make a list:

isEmpty :: [a] -> Bool


isEmpty [] = True
isEmpty (x:xs) = False

We will look at all of these in detail now.

On Booleans
One of the simplest patterns is to match for Booleans.

If the input is just one Boolean, there are only two patterns:

notB' :: Bool -> Bool


notB' False = True
notB' True = False

If a function takes two Booleans as input, there are 2^2 = 4 patterns:

andB :: Bool -> Bool -> Bool


andB True True = True
andB True False = False
andB False True = False
andB False False = False

The last three patterns can be combined. Here, the wildcard pattern _ matches anything, and discards it:

andB' :: Bool -> Bool -> Bool


andB' True True = True
andB' _ _ = False

There is a difference between these two versions: in the latter, if the first argument is False , then the second argument does not need to be evaluated:
False is returned immediately.

In the next example, the pattern b matches anything. However, in contrast to _ , we can use b on the right-hand side of = :

andB'' :: Bool -> Bool -> Bool


andB'' True b = b
andB'' False _ = False
Exercise: Write a function orB :: Bool -> Bool -> Bool that returns True if at least one argument is True .

Non-exhaustive patterns
Consider the following example:

isTrue :: Bool -> Bool


isTrue True = True

Question: What will isTrue False evaluate to?

Answer: This is a non-exhaustive pattern, and isTrue False will raise an exception:

*Main> isTrue False


*** Exception: defining-functions.hs:36:1-18: Non-exhaustive patterns in function isTrue

We can choose to throw a custom-made exception instead:

isTrue' :: Bool -> Bool


isTrue' True = True
isTrue' False = error "not True"

On tuples
If the function we are defining expects as input a tuple, we can match against the individual components:

fst :: (a,b) -> a


fst (x,y) = x

We actually don't use y in the output of the function, so we can use fst (x,_) = x instead. Similarly,

snd :: (a,b) -> b


snd (_,y) = y

This generalizes to tuples of three or more components:

third :: (a, b, c) -> c


third (_, _, z) = z

We can match several tuples at the same time:

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)


addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

Exercise: Write a function swap :: (a, b) -> (b, a) that swaps the elements of a pair.

On lists
See also this video.

All lists are built by prepending (:) , successively, elements to an existing list, starting with the empty list [] . That means, the list [1, 2, 3] has been
obtained as 1:[2,3] , etc. - [1, 2, 3] is short for 1:(2:(3:[])) . In other words, every list in [a] is either

 the empty list; or


 of the form x:xs for x :: a and xs :: [a] .

isEmpty' :: [a] -> Bool


isEmpty' [] = True
isEmpty' (x:xs) = False

We are not actually using x or xs in the output of the second pattern, so we can write the function more simply as

isEmpty'' :: [a] -> Bool


isEmpty'' [] = True
isEmpty'' (_:_) = False

Note that the parentheses around _:_ in the second pattern are necessary!

We can write more complex list patterns. To return the second element of a list:
sndElem :: [a] -> a
sndElem (_:x:_) = x

Case expressions
The aforementioned patterns are special forms, for Booleans, and lists. The general form for such pattern matching is via case expressions:

isEmpty2 :: [a] -> Bool


isEmpty2 x = case x of [] -> True
(_:_) -> False

Here, it is important that all the patterns are exactly aligned, i.e., the [] and (_:_) must start in exactly the same column.

Lambda expressions
Lambda expressions are nameless functions. They are particularly useful in higher-order functions, which will be discussed in a later lesson. A video
accompanying this section is here.

Lambda expressions are of the form \<input variables> -> <output> . For instance, we can define a function that returns its double as \x -> 2 * x .
Here, the input variable is indicated by the backslash \ . After the arrow -> , the output of the function is specified. ( \ stands for the greek letter λ
(lambda), see Wikipedia). Thus, the following definitions are equivalent:

double :: Int -> Int


double x = 2 * x

double' :: Int -> Int


double' = \x -> 2 * x

Lambda expressions can have several input variables:

mult :: Int -> Int -> Int


mult x y = x * y

mult' :: Int -> Int -> Int


mult' = \x y -> x * y

Here, the second variant is a short form of

mult'' :: Int -> (Int -> Int)


mult'' = \x -> (\y -> x * y)

Just like a pattern can ignore (part of) the input, a lambda expression can ignore its input

alwaysZero :: Bool -> Int


alwaysZero = \_ -> 0

One important application of lambda expressions are higher-order functions, where functions are arguments to other functions. Consider

apply :: (a -> b) -> a -> b


apply f x = f x

*Main> apply (\_ -> 5) 'r'


5
*Main> apply (\ x -> if x < 0 then "Less than zero!" else "Greater or equal than zero!") (-3)
"Less than zero!"

Operators and sections


There is also a video on operators and sections.

When a function has two arguments, such as (:) , we can write it infix, between its two arguments. A function that is used infix (hence necessary binary)
is called an operator.

 Any binary function can be turned into an operator by enclosing it in backticks. E.g. div 7 2 can be written 7 `div` 2 .
 Conversely, any operator can be used prefix by enclosing it in parentheses, e.g., (:) 1 [2,3] .

Every operator ⊗ with inputs of type a and b and output of type c gives rise to three sections:
 (⊗) :: a -> b -> c . Here, (⊗) = \x y -> x ⊗ y .
 (x ⊗) :: b -> c , where x :: a . Here, (x ⊗) = \y -> x ⊗ y .
 (⊗ y) :: a -> c , where y :: b . Here, (⊗ y) = \x -> x ⊗ y .

Sections can be used to concisely define functions:

square :: Int -> Int


square = (^2)

reci :: Fractional a => a -> a


reci = (1 /)

Remarks:

 An operator ⊗ by itself is not a valid Haskell expression: it needs to be used as a section, e.g., (⊗) .
 Sections are useful when programming with higher-order functions (cf. later lesson.).

Exercises
(Adapted and expanded from the book "Programming in Haskell)

 Define three variants of a function third :: [a] -> a that returns the third element in any list that contains at least this many elements, using
 head and tail
 list indexing !!
 pattern matching
 Define a function safetail :: [a] -> [a] that behaves like tail except that it maps [] to [] (instead of throwing an error). Using tail and
isEmpty :: [a] -> Bool , define safetail using
 a conditional expression
 guarded equations
 pattern matching

See also

 Chapter 3, "Syntax in Functions" of "Learn You a Haskell"


 Haskell Wiki on Sections

Summary

 We have seen several ways to define functions: composition, conditionals, guard equations, pattern matching, lambda expressions.
 When patterns are not exhaustive, functions raise an exception whenever no pattern matches. To avoid this, one may use a catch-all otherwise
pattern at the end.
 Any pattern matching can be expressed using a case expression.
 Anonymous functions can concisely be written using lambda expressions.

You might also like