Haskell/More on functions
As functions are absolutely essential to functional programming, there are several nice features that make using functions easier.
let and where revisited
First, a few extra words about
where, which are useful to make local function definitions. A function like
addStr from the List processing chapter...
addStr :: Float -> String -> Float addStr x str = x + read str sumStr :: [String] -> Float sumStr = foldl addStr 0.0
... can be rewritten using local bindings in order to reduce clutter on the top level of the program (which makes a lot of sense assuming
addStr is only used as part of
sumStr). We can do that either with a let binding...
sumStr = let addStr x str = x + read str in foldl addStr 0.0
... or with a
sumStr = foldl addStr 0.0 where addStr x str = x + read str
... and the difference appears to be only one of style - bindings coming either before or after the rest of the definition. The relation between
where, however, is similar to the one between
if and guards, in that a let...in construct is an expression while a
where clause isn't. That means we can embed a
let binding, but not a
where clause, in a complex expression in this way:
f x = if x > 0 then (let lsq = (log x) ^ 2 in tan lsq) * sin x else 0
The expression within the outer parentheses is self-contained, and evaluates to the tangent of the square of the logarithm of
x. Note that the scope of
lsq does not extend beyond the parentheses; and therefore changing the then-branch to
then (let lsq = (log x) ^ 2 in tan lsq) * (sin x + lsq)
wouldn't work - we would have to drop the parentheses around the
where clauses can be incorporated into
describeColour c = "This colour " ++ case c of Black -> "is black" White -> "is white" RGB red green blue -> " has an average of the components of " ++ 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 with guards:
doStuff :: Int -> String doStuff x | x < 3 = report "less than three" | otherwise = report "normal" where report y = "the input is " ++ y
Note that since there is one equals sign for each guard there is no place we could put a
let expression which would be in scope of all guards, as the
where clause is. Here we have a situation in which
where is particularly convenient.
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,
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 - as cramming complicated expressions in a lambda can hurt readability.
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
tail with a lambda:
tail' = (\(_:xs) -> xs)
Operators and sections
As noted in a number of occasions, operators such as the arithmetical ones can be used surrounded in parentheses and used prefix, like other functions:
2 + 4 (+) 2 4
Generalizing that point, we can now define the term "operator" clearly: as far as Haskell is concerned it's a function with two arguments and a name consisting entirely of non-alphanumeric characters. Unlike other functions, operators can be used infix straight away. We can define new operators in the usual way; just don't use any alphanumeric characters. For example, here's the set-difference definition from
(\\) :: (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 write them with the parentheses.
Sections are a nifty piece of syntactical sugar that can be used with operators. An operator within parentheses and flanked by one of its arguments...
(2+) 4 (+4) 2
... is a new function in its own right.
(2+), for instance, has the type
(Num a) => a -> a. We can pass sections to other functions, e.g.
map (+2) [1..4] == [3..6]. For another example, we can add an extra flourish to the
multiplyList function we wrote back in More about lists:
multiplyList :: Integer -> [Integer] -> [Integer] multiplyList m = map (m*)
If you have a "normal", 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. 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 (that is, those that take two arguments) infix. Think about the functions you use, and see which ones would read better if you used them infix.