Haskell/More on functions
As functions are absolutely essential to functional programming, there are some nice features you can use to make using functions easier.
[edit] Private functions - let and where
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 if it is only used as part of the sumStr. Haskell lets you nest declarations in two subtly different ways: either using the let bindings we already know:
sumStr = let addStr x str = x + read str in foldl addStr 0.0
or with where clauses, which work in pretty much the same way but come after the expression which use the function.
sumStr = foldl addStr 0.0 where addStr x str = x + read str
You can also use let and where in case expression and similar control structures, just as you can in functions:
describeColour c = "This colour is " ++ case c of Black -> "black" White -> "white" RGB red green blue -> "the average of the components is " ++ show av where av = (red + green + blue) `div` 3 ++ ", yeah?"
In this example, the indentation of the where clause sets the scope of the av variable so that it only exists as far as the RGB red green blue case is concerned. Placing it at the same indentation of the cases would make it available for all cases. Here is an example of such an usage with guards:
doStuff :: Int -> String doStuff x | x < 3 = report "less than three" | otherwise = report "normal" where report y = "the input is " ++ y
[edit] Anonymous Functions - lambdas
An alternative to creating a private 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 expression 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, which evaluates to "x + read str". So, the sumStr presented just above is precisely the same as the one that used addStr in a let binding.
Lambdas are handy for writing one-off functions to be used with maps, folds and their siblings, especially where the function in question is simple. The example above is about as complicated as you'll want them to be – cramming more verbose constructs like conditionals in such a compact syntactic form could get messy.
Since variables are being bound in a lambda expression (to the arguments, just like in a regular function definition), pattern matching can be used in them as well. A trivial example would be redefining head with a lambda:
head = (\(x:xs) -> x)
[edit] Infix versus Prefix
As we noted previously, 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] == [3..7].
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 |
|---|
|
This page may need to be