Haskell - Functions


Advertisements


Functions play a major role in Haskell, as it is a functional programming language. Like other languages, Haskell does have its own functional definition and declaration.

  • Function declaration consists of the function name and its argument list along with its output.

  • Function definition is where you actually define a function.

Let us take small example of add function to understand this concept in detail.

Live Demo
add :: Integer -> Integer -> Integer   --function declaration 
add x y =  x + y                       --function definition 

main = do 
   putStrLn "The addition of the two numbers is:"  
   print(add 2 5)    --calling a function 

Here, we have declared our function in the first line and in the second line, we have written our actual function that will take two arguments and produce one integer type output.

Like most other languages, Haskell starts compiling the code from the main method. Our code will generate the following output −

The addition of the two numbers is:
7

Pattern Matching

Pattern Matching is process of matching specific type of expressions. It is nothing but a technique to simplify your code. This technique can be implemented into any type of Type class. If-Else can be used as an alternate option of pattern matching.

Pattern Matching can be considered as a variant of dynamic polymorphism where at runtime, different methods can be executed depending on their argument list.

Take a look at the following code block. Here we have used the technique of Pattern Matching to calculate the factorial of a number.

Live Demo
fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5)

We all know how to calculate the factorial of a number. The compiler will start searching for a function called "fact" with an argument. If the argument is not equal to 0, then the number will keep on calling the same function with 1 less than that of the actual argument.

When the pattern of the argument exactly matches with 0, it will call our pattern which is "fact 0 = 1". Our code will produce the following output −

The factorial of 5 is:
120

Guards

Guards is a concept that is very similar to pattern matching. In pattern matching, we usually match one or more expressions, but we use guards to test some property of an expression.

Although it is advisable to use pattern matching over guards, but from a developer’s perspective, guards is more readable and simple. For first-time users, guards can look very similar to If-Else statements, but they are functionally different.

In the following code, we have modified our factorial program by using the concept of guards.

Live Demo
fact :: Integer -> Integer 
fact n | n == 0 = 1 
       | n /= 0 = n * fact (n-1) 
main = do 
   putStrLn "The factorial of 5 is:"  
   print (fact 5) 

Here, we have declared two guards, separated by "|" and calling the fact function from main. Internally, the compiler will work in the same manner as in the case of pattern matching to yield the following output −

The factorial of 5 is:
120

Where Clause

Where is a keyword or inbuilt function that can be used at runtime to generate a desired output. It can be very helpful when function calculation becomes complex.

Consider a scenario where your input is a complex expression with multiple parameters. In such cases, you can break the entire expression into small parts using the "where" clause.

In the following example, we are taking a complex mathematical expression. We will show how you can find the roots of a polynomial equation [x^2 - 8x + 6] using Haskell.

Live Demo
roots :: (Float, Float, Float) -> (Float, Float)  
roots (a,b,c) = (x1, x2) where 
   x1 = e + sqrt d / (2 * a) 
   x2 = e - sqrt d / (2 * a) 
   d = b * b - 4 * a * c  
   e = - b / (2 * a)  
main = do 
   putStrLn "The roots of our Polynomial equation are:" 
   print (roots(1,-8,6))

Notice the complexity of our expression to calculate the roots of the given polynomial function. It is quite complex. Hence, we are breaking the expression using the where clause. The above piece of code will generate the following output −

The roots of our Polynomial equation are:
(7.1622777,0.8377223)

Recursion Function

Recursion is a situation where a function calls itself repeatedly. Haskell does not provide any facility of looping any expression for more than once. Instead, Haskell wants you to break your entire functionality into a collection of different functions and use recursion technique to implement your functionality.

Let us consider our pattern matching example again, where we have calculated the factorial of a number. Finding the factorial of a number is a classic case of using Recursion. Here, you might, "How is pattern matching any different from recursion?” The difference between these two lie in the way they are used. Pattern matching works on setting up the terminal constrain, whereas recursion is a function call.

In the following example, we have used both pattern matching and recursion to calculate the factorial of 5.

Live Demo
fact :: Int -> Int 
fact 0 = 1 
fact n = n * fact ( n - 1 ) 

main = do 
   putStrLn "The factorial of 5 is:" 
   print (fact 5) 

It will produce the following output −

The factorial of 5 is:
120

Higher Order Function

Till now, what we have seen is that Haskell functions take one type as input and produce another type as output, which is pretty much similar in other imperative languages. Higher Order Functions are a unique feature of Haskell where you can use a function as an input or output argument.

Although it is a virtual concept, but in real-world programs, every function that we define in Haskell use higher-order mechanism to provide output. If you get a chance to look into the library function of Haskell, then you will find that most of the library functions have been written in higher order manner.

Let us take an example where we will import an inbuilt higher order function map and use the same to implement another higher order function according to our choice.

Live Demo
import Data.Char  
import Prelude hiding (map) 

map :: (a -> b) -> [a] -> [b] 
map _ [] = [] 
map func (x : abc) = func x : map func abc  
main = print $ map toUpper "tutorialspoint.com" 

In the above example, we have used the toUpper function of the Type Class Char to convert our input into uppercase. Here, the method "map" is taking a function as an argument and returning the required output. Here is its output −

sh-4.3$ ghc -O2 --make *.hs -o main -threaded -rtsopts
sh-4.3$ main
"TUTORIALSPOINT.COM" 

Lambda Expression

We sometimes have to write a function that is going to be used only once, throughout the entire lifespan of an application. To deal with this kind of situations, Haskell developers use another anonymous block known as lambda expression or lambda function.

A function without having a definition is called a lambda function. A lambda function is denoted by "\" character. Let us take the following example where we will increase the input value by 1 without creating any function.

Live Demo
main = do 
   putStrLn "The successor of 4 is:"  
   print ((\x -> x + 1) 4)

Here, we have created an anonymous function which does not have a name. It takes the integer 4 as an argument and prints the output value. We are basically operating one function without even declaring it properly. That's the beauty of lambda expressions.

Our lambda expression will produce the following output −

sh-4.3$ main
The successor of 4 is:
5


Advertisements