Haskell/More on functions

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search



More on functions (Solutions)

Contents

Elementary Haskell

Recursion
List processing
More about lists
Pattern matching
Control structures
More on functions
Higher order functions Image:50%.png

As functions are absolutely essential to functional programming, there are some nice features you can use to make using functions easier.

[edit] Private Functions

Remember the sumStr function from the chapter on list processing. It used another function called addStr:

addStr :: Float -> String -> Float
addStr x str = x + read str

sumStr :: [String] -> Float
sumStr = foldl addStr 0.0

So you could find that

  addStr 4.3 "23.7"

gives 28.0, and

  sumStr ["1.2", "4.3", "6.0"]

gives 11.5.

But maybe you don't want addStr cluttering up the top level of your program. Haskell lets you nest declarations in two subtly different ways:

 sumStr = foldl addStr 0.0
    where addStr x str = x + read str
 sumStr =
    let addStr x str = x + read str
    in foldl addStr 0.0

The difference between let and where lies in the fact that let foo = 5 in foo + foo is an expression, but foo + foo where foo = 5 is not. (Try it: an interpreter will reject the latter expression.) Where clauses are part of the function declaration as a whole, which makes a difference when using guards.

[edit] Anonymous Functions

An alternative to creating a named function like addStr is to create an anonymous function, also known as a lambda function. For example, sumStr could have been defined like this:

 sumStr = foldl (\x str -> x + read str) 0.0

The bit in the parentheses is a lambda function. The backslash is used as the nearest ASCII equivalent to the Greek letter lambda (λ). This example is a lambda function with two arguments, x and str, and the result is "x + read str". So, the sumStr presented just above is precisely the same as the one that used addStr in a let binding.

Lambda functions are handy for one-off function parameters, especially where the function in question is simple. The example above is about as complicated as you want to get.

[edit] Infix versus Prefix

As we noted in the previous chapter, you can take an operator and turn it into a function by surrounding it in brackets:

2 + 4
(+) 2 4

This is called making the operator prefix: you're using it before its arguments, so it's known as a prefix function. We can now formalise the term 'operator': it's a function which entirely consists of non-alphanumeric characters, and is used infix (normally). You can define your own operators just the same as functions, just don't use any alphanumeric characters. For example, here's the set-difference definition from Data.List:

(\\) :: Eq a => [a] -> [a] -> [a]
xs \\ ys = foldl (\zs y -> delete y zs) xs ys

Note that aside from just using operators infix, you can define them infix as well. This is a point that most newcomers to Haskell miss. I.e., although one could have written:

(\\) xs ys = foldl (\zs y -> delete y zs) xs ys

It's more common to define operators infix. However, do note that in type declarations, you have to surround the operators by parentheses.

You can use a variant on this parentheses style for 'sections':

(2+) 4
(+4) 2

These sections are functions in their own right. (2+) has the type Int -> Int, for example, and you can pass sections to other functions, e.g. map (+2) [1..4].

If you have a (prefix) function, and want to use it as an operator, simply surround it by backticks:

1 `elem` [1..4]

This is called making the function infix: you're using it in between its arguments. It's normally done for readability purposes: 1 `elem` [1..4] reads better than elem 1 [1..4]. You can also define functions infix:

elem :: Eq a => a -> [a] -> Bool
x `elem` xs = any (==x) xs

But once again notice that in the type signature you have to use the prefix style.

Sections even work with infix functions:

(1 `elem`) [1..4]
(`elem` [1..4]) 1

You can only make binary functions (those that take two arguments) infix. Think about the functions you use, and see which ones would read better if you used them infix.

Exercises
  • Lambdas are a nice way to avoid defining unnecessary separate functions. Convert the following let- or where-bindings to lambdas:
    • map f xs where f x = x * 2 + 3
    • let f x y = read x + y in foldr f 1 xs
  • Sections are just syntactic sugar for lambda operations. I.e. (+2) is equivalent to \x -> x + 2. What would the following sections 'desugar' to? What would be their types?
    • (4+)
    • (1 `elem`)
    • (`notElem` "abc")
Personal tools
Create a book