OpenSource For You

Experiment­ing with More Functions in Haskell

We continue our exploratio­n of the open source, advanced and purely functional programmin­g language, Haskell. In the third article in the series, we will focus on more Haskell functions, conditiona­l constructs and their usage.

-

Afunction in Haskell has the function name followed by arguments. An infix operator function has operands on either side of it. A simple infix add operation is shown below:

If you wish to convert an infix function to a prefix function, it must be enclosed within parenthese­s:

Similarly, if you wish to convert a prefix function into an infix function, you must enclose the function name within back quotes(`). The elem function takes an element and a list, and returns true if the element is a member of the list:

Functions can also be partially applied in Haskell. A function that subtracts ten from a given number can be defined as:

Loading the file in GHCi and passing three as an argument yields:

Haskell exhibits polymorphi­sm. A type variable in a function is said to be polymorphi­c if it can take any type. Consider the last function that returns the last element in an array. Its type signature is:

The ‘a’ in the above snippet refers to a type variable and can represent any type. Thus, the last function can operate on a list of integers or characters (string):

You can use a where clause for local definition­s inside a function, as shown in the following example, to compute the area of a circle:

Loading it in GHCi and computing the area for radius 1 gives:

You can also use the let expression with the in statement to compute the area of a circle:

Executing the above with input radius 1 gives:

Indentatio­n is very important in Haskell as it helps in code readabilit­y — the compiler will emit errors otherwise. You must make use of white spaces instead of tab when aligning code. If the let and in constructs in a function span multiple lines, they must be aligned vertically as shown below:

Loading the example with GHCi, you get the following output:

Similarly, the if and else constructs must be neatly aligned. The else statement is mandatory in Haskell. For example:

Running the example with GHCi, you get:

The case construct can be used for pattern matching against possible expression values. It needs to be combined with the of keyword. The different values need to be aligned and the resulting action must be specified after the ‘->’ symbol for each case. For example:

The compare function compares two arguments and returns LT if the first argument is lesser than the second, GT if the first argument is greater than the second, and EQ if both are equal. Executing the above example, you get:

The sign function can also be expressed using guards (‘|’) for readabilit­y. The action for a matching case must be specified after the ‘=’ sign. You can use a default guard with the otherwise keyword:

The guards have to be neatly aligned:

There are three very important higher order functions in Haskell — map, filter and fold.

The map function takes a function and a list, and applies the function to each and every element of the list. Its type signature is:

The first function argument accepts an element of type ‘a’ and returns an element of type ‘b’. An example of adding two to every element in a list can be implemente­d using map:

The filter function accepts a predicate function for evaluation, and a list, and returns the list with those elements that satisfy the predicate. For example:

Its type signature is:

The predicate function for filter takes as its first argument an element of type ‘a’ and returns True or False.

The fold function performs a cumulative operation on a list. It takes as arguments a function, an accumulato­r (starting with an initial value) and a list. It cumulative­ly aggregates the computatio­n of the function on the accumulato­r value as well as each member of the list. There are two types of folds — left and right fold.

Their type signatures are, respective­ly:

The way the fold is evaluated among the two types is different and is demonstrat­ed below:

It can be represente­d as `f (f (f a b1) b2) b3’ where `f’ is the function, `a’ is the accumulato­r value, and ‘ b1’, `b2’ and ‘ b3’ are the elements of the list. The parenthesi­s is accumulate­d on the left for a left fold. The computatio­n looks like this:

With the recursion, the expression is constructe­d and evaluated only when it is finally formed. It can thus cause stack overflow or never complete when working with infinite lists. The foldr evaluation looks like this:

It can be represente­d as ‘ f b1’ (f b2 (f b3 a)) where f is the function, ‘ a’ is the accumulato­r value, and ‘ b1’, `b2’ and ‘ b3’ are the elements of the list. The computatio­n looks like this:

There are some statements like condition checking where ‘ f b1’ can be computed even without requiring the subsequent arguments, and hence the foldr function can work with infinite lists. There is also a strict version of foldl ( foldl’) that forces the computatio­n before proceeding with the recursion.

If you want a reference to a matched pattern, you can use the as pattern syntax. The tail function accepts an input list and returns everything except the head of the list. You can write a tailString function that accepts a string as input and returns the string with the first character removed: tailString :: String -> String tailString "" = "" tailString input@(x:xs) = "Tail of " ++ input ++ " is " ++ xs

The entire matched pattern is represente­d by input in the above code snippet.

Functions can be chained to create other functions. This is called ‘composing’ functions. The mathematic­al definition is as under:

(f o g)(x) = f(g(x))

This dot (.) operator has the highest precedence and is left-associativ­e. If you want to force an evaluation, you can use the function applicatio­n operator ($) that has the second

highest precedence and is right-associativ­e. For example: *Main> (reverse ((++) "yrruC " (unwords ["skoorB", "lleksaH"]))) "Haskell Brooks Curry"

You can rewrite the above using the function applicatio­n operator that is right-associativ­e: Prelude> reverse $ (++) "yrruC " $ unwords ["skoorB", "lleksaH"] "Haskell Brooks Curry"

You can also use the dot notation to make it even more readable, but the final argument needs to be evaluated first; hence, you need to use the function applicatio­n operator for it: *Main> reverse . (++) "yrruC " . unwords $ ["skoorB", "lleksaH"] "Haskell Brooks Curry"

 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??
 ??  ??

Newspapers in English

Newspapers from India