REVISED: Thursday, October 17, 2024
Writing Haskell Functions.
I. WRITING HASKELL FUNCTIONS
Haskell is sensitive to indentation and spacing.
A. FOLLOW GOOD CODING STYLE
1. Follow the 80 column rule: a line of code must not exceed 80 columns.
2. Avoid too many parentheses by using ($) and (.) function composition.
3. Use {- for large multi-lined comments. -}
4. Use type synonyms to make code readable.
B. DEFINE THE TYPE
We will discuss a Haskell function writing process by using the process to write a function which will provide the product of the elements of a list.
products :: [Int] -> Int -- Function signature.
C. ENUMERATE THE CASES
For most types of argument, there are a number of standard cases to consider. For non-negative integers, the standard cases are 0 and n+1, for logical values they are False and True, and so on.
For lists, the standard cases are the empty list and non-empty lists.
For lists, the standard cases are the empty list and non-empty lists.
products [] =
products (n : ns) =
products (n : ns) =
D. DEFINE THE SIMPLE CASES
By definition, the product of zero integers is one, because one is the identity for multiplication. Hence it is straightforward to define the empty list case:
products [] = 1
products (n : ns) =
As in this example, the simple cases often become base cases for recursion.
E. DEFINE THE OTHER CASES
How can we calculate the product of a non-empty list of integers? For this step, it is useful to consider the ingredients that can be used, such as the product function itself, the n and ns arguments, and library functions of relevant types: +, -, *, and so on. In this case, we simply multiply the first integer and the product of the remaining list of integers:
products [] = 1 -- Base case.
products (n : ns) = n * products ns -- Recursive case.
As in this example, the other cases often become recursive cases.
F. GENERALISE AND SIMPLIFY
Once a function has been defined using the above process, it often becomes clear that it can be generalized and simplified. For example, the function products does not depend on the precise kind of numbers to which it is applied, so its type can be generalized from integers to any numeric type:
In terms of simplification, we see that the pattern of recursion used in products is encapsulated by a library function called foldr. Using folder, products can be redefined by a single equation:
In conclusion, our final definition for product is as follows:
products :: Num a => [a] -> a -- Function signature.
products = foldr (*) 1
"Copy Paste" the above products function into your text editor and "File, Save As" products.hs to your working directory.
II. USING GHCI TO VALIDATE HASKELL FUNCTIONS
"Copy Paste" the following module example into your text editor:
products :: Num a => [a] -> a -- Function signature.
products = foldr (*) 1
The example above should be saved as Products.hs.
Open GHCi and load the module Products as shown below:
Prelude> :load Products
[1 of 1] Compiling Products ( Products.hs, interpreted )
Ok, modules loaded: Products.
Prelude>
Since the function signature for Products shows a list of numbers mapped to a single number; therefore, we have to use a list of arguments when we call the function in GHCi, as shown below:
Prelude> products [1,2,3,4]
24
it :: Num a => a
Prelude>
Prelude> import Data.List
Prelude>
Prelude> let f = nub [1,1,1,2,2,2,3,3,3]
f :: (Num a, Eq a) => [a]
Prelude>
Prelude> f
it :: (Num a, Eq a) => [a]
V. FACTORS
factors :: Int -> [Int]
factors n = [x | x <- [1..n], n `mod` x == 0]
prime :: Int -> Bool
prime n = factors n == [1,n]
primes :: Int -> [Int]
primes n = [x | x <- [2..n], prime x]
The example above should be saved as factors.hs to your working directory.
Open GHCi and load factors as shown below:
Prelude> :l factors.hs
[1 of 1] Compiling Main ( factors.hs, interpreted )
Ok, one module loaded.
*Main> factors 15
[1,3,5,15]
*Main> prime 15
False
*Main> primes 15
[2,3,5,7,11,13]
*Main>
VI. CONCLUSION
Prelude>
III. IDENTITY
Identity in mathematics refers to a special element or value within a set or structure that leaves other elements unchanged when combined with it using a particular operation. This concept is fundamental to various mathematical fields.
Here are some key aspects of identity:
Neutral Element: It's often called a "neutral element" because it doesn't change the value of other elements.
Operation-Specific: The identity element is defined in relation to a specific operation, such as addition, multiplication, or a more abstract operation.
Inverse Elements: Identity elements are closely related to inverse elements. For example, in addition, the additive identity is 0, and the additive inverse of a number x is -x.
Examples of identity in different mathematical contexts:
Addition: The additive identity is 0. For any number x, x + 0 = x.
Multiplication: The multiplicative identity is 1. For any number x, x * 1 = x.
Matrix Multiplication: The identity matrix, denoted by I, is a square matrix with 1s on the diagonal and 0s elsewhere. For any matrix A, A * I = A.
Group Theory: A group has an identity element, denoted by e, such that for any element g in the group, g * e = e * g = g.
In summary, identity is a crucial concept in mathematics that provides a foundation for understanding various algebraic structures and operations. It ensures that certain operations leave elements unchanged, allowing for consistent and predictable calculations.
True is the identity for && and.
False is the identity for || or.
1 is the identity for * multiplication.
0 is the identity for + addition.
[] empty list is the identity for ++ append.
False is the identity for || or.
1 is the identity for * multiplication.
0 is the identity for + addition.
[] empty list is the identity for ++ append.
IV. REMOVING DUPLICATES
Prelude> import Data.List
Prelude>
Prelude> let f = nub [1,1,1,2,2,2,3,3,3]
f :: (Num a, Eq a) => [a]
Prelude>
Prelude> f
[1,2,3]
Prelude>
V. FACTORS
"Copy Paste" the following example into your text editor:
factors :: Int -> [Int]
factors n = [x | x <- [1..n], n `mod` x == 0]
prime :: Int -> Bool
prime n = factors n == [1,n]
primes :: Int -> [Int]
primes n = [x | x <- [2..n], prime x]
The example above should be saved as factors.hs to your working directory.
Open GHCi and load factors as shown below:
Prelude> :l factors.hs
[1 of 1] Compiling Main ( factors.hs, interpreted )
Ok, one module loaded.
*Main> factors 15
[1,3,5,15]
*Main> prime 15
False
*Main> primes 15
[2,3,5,7,11,13]
*Main>
VI. CONCLUSION
In this tutorial, you have learned a process for writing Haskell functions.
VII. REFERENCES
Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.
Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.
Goerzen, J. & O'Sullivan, B. & Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.
Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.
Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.
Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.