Showing posts with label filter. Show all posts
Showing posts with label filter. Show all posts

Thursday, May 21, 2015

HASKELL HIGHER ORDER FUNCTIONS

HASKELL HIGHER ORDER FUNCTIONS

REVISED: Tuesday, October 15, 2024




1. INTRODUCTION

A function is called higher order if it takes a function as an argument or returns a function as a result. In Haskell, we refer to functions that take other functions as arguments and return new functions as combinators.

Higher order functions taking functions as arguments include map and filter.

The term curried is normally used for higher order functions that take their arguments one at a time and return a function as a result.

Haskell higher order functions can be used to define Embedded Domain Specific Languages (EDSLs).

2. MAP

map applies a function to every element of a list.

2.1. MAP DEFINED USING LIST COMPREHENSION

Defining a map using list comprehension makes it easy to see how a map applies a function f x to every element of a list of xs:

-- Functions passed to other functions are written with their type declarations surrounded by parentheses; i.e., (a -> b).
-- 1st argument is a function that takes an "a" and returns a "b".
map :: (a -> b) -> [a] -> [b]
map f xs = [f x | x <- xs]

2.2. MAP DEFINED USING RECURSION

Haskell programmers never use loops. Instead they either use recursion to do looping, or they use functions like map that take other functions as arguments.

Defining map using recursion:

map :: (a -> b) -> [a] -> [b]
map f []        = []
map f (x:xs) = f x : map f xs

3. FILTER

filter selects every element from a list that satisfies a condition or predicate.

3.1. FILTER DEFINED USING LIST COMPREHENSION

Given:
p x is a condition or predicate function.
xs is a list of xs.
x is an element drawn from the list of xs.
<- is pronounced "drawn from."
| pipe is pronounced "such that."
, comma before the condition or predicate is pronounced "such that."

filter defined using list comprehension:

filter :: (a -> Bool) -> [a] -> [a]
filter p xs = [x | x <- xs , p x]

3.2. FILTER DEFINED USING RECURSION

filter :: (a -> Bool) -> [a] -> [a]
filter p [] = []
filter p (x:xs)
    | px            = x : filter p xs
    | otherwise = filter p xs

4. CURRIED

Functions with multiple arguments are defined in Haskell by currying. Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed.

In Haskell, all functions are considered curried. All functions in Haskell take just single arguments.

Curried functions can be partially applied. Partial application in Haskell involves passing less than the full number of arguments to a function that takes multiple arguments.

5. EMBEDDED DOMAIN SPECIFIC LANGUAGES (EDSLs)

Higher-order functions can be used in Haskell to define EDSLs which can be used to do many things including processing lists and building parsers. A Haskell EDSL is a language embedded inside of Haskell. An EDSL uses the library of functions provided by Haskell, often called combinators, because they combine their arguments into terms inside the EDSL. In essence, EDSLs are just Haskell libraries.

6. USER DEFINED

User-defined higher-order functions often use partial application. Partial application in Haskell involves passing less than the full number of arguments to a function that takes multiple arguments. This results in a new function taking the remaining number of parameters.

6.1. RECEIVE A FUNCTION

The function area is missing the value for the radius rThe radius function computes the area of the circle using a hard-coded value of 5 for the radius r. This is done by the function radius argument func receiving the function area with argument 5 as shown below.

-- C:\Users\Tinnel\areaCircle.hs

import Data.List
import System.IO

-- Area of Circle
area :: Floating a => a -> a
area r = pi * r ^ 2  -- The function area is missing the value for the radius r.

radius :: Num a => (a -> t) -> t  -- func receives area function (a -> t).
radius func = func 5 -- radius argument 5, the constant t, is passed into the function area.

areaCircle :: Double
areaCircle = radius area  -- areaCircle stores results of function radius with r of 5.

Prelude> areaCircle
78.53981633974483
Prelude> 

6.2. RETURN A FUNCTION

-- C:\Users\Tinnel\onePlus9.hs

import Data.List
import System.IO

addFunc :: Num a => a -> a -> a  -- When you type :t addFunc
addFunc x y = x + y

{-

The -> kleisli arrow operator is right-associative, and the function application is left-associative, meaning the type signature of addFunc actually looks like this:

addFunc :: Num a => a -> (a -> a)

This means that addFunc actually takes one argument and returns a function that takes another argument and returns an Int. In other words, addFunc is a function that takes a number and returns a function that takes a number and returns the sum of the two numbers.

-}

add9 :: Integer -> Integer
add9 = addFunc 9

onePlus9 :: Integer
onePlus9 = add9 1 

Prelude> onePlus9
10
Prelude> 

7. CONCLUSION

Haskell higher-order functions allow for powerful abstractions. They make it easier to think in terms of functions and design the ultimate abstraction of Embedded Domain Specific Languages (EDSLs).

  8. 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.




Tuesday, May 14, 2013

HASKELL LIST COMPREHENSION

HASKELL LIST COMPREHENSION

REVISED: Tuesday, February 6, 2024




Haskell List Comprehension.

I. HASKELL LIST COMPREHENSION

A. HASKELL LIST COMPREHENSION EXAMPLE

[ x | x <- S,  p x ]

For every x such that x is an element of the set S and satisfies the predicate p x.

| pipe is pronounced "such that".
<- arrow is pronounced "drawn from".
S set is pronounced "generator".
, comma before the predicate is pronounced "if".
p x is pronounced "condition" or "predicate".

In the example shown below,  x is a function, such that every x drawn from the generator [0,1,2,3,4,5,6,7,8,9] is less than or equal to five.

Prelude>  [ x | x <- [0,1,2,3,4,5,6,7,8,9], x <= 5 ]
[0,1,2,3,4,5]
it :: (Ord t, Num t) => [t]
Prelude>  

The part before the pipe |, is the output function x. The part after the pipe |, is the qualifier. x is drawn from the input set generator [0,1,2,3,4,5,6,7,8,9]. For every element in the generator [0,1,2,3,4,5,6,7,8,9] that we have bound to x we get that element x if it satisfies the condition, predicate, guard of being <= 5.

B. HASKELL LIST COMPREHENSION GUARDS

A guard is a filter, a boolean predicate that evaluates to True or False; for example, odd , isAlpha, and all of the boolean expressions such as >, <, ==, etc.

You can use as many generators and guards as you want in a list comprehension.

C. HASKELL EMPTY LIST

You always want to know if a function is associative, (a + b) + c  =  a + (b + c); and its identity element.

Prelude>  sum []   -- 0 is the neutral element or identity for sum.
0
it :: Num a => a
Prelude>

Prelude>  product []   -- 1 is the neutral element or identity for times.
1
it :: Num a => a
Prelude>  

D. HASKELL LIST COMPREHENSION GENERATOR EXAMPLE

Prelude>  let y = [ x*x | x <- [1..3] ]
y :: (Num t, Enum t) => [t]
Prelude>

Prelude>  y
[1,4,9]
it :: (Num t, Enum t) => [t]
Prelude>

Prelude>  :type y
y :: (Num t, Enum t) => [t]
Prelude>  

[ x*x | x <- [1..3] ]
=
[ 1*1] ++ [ 2*2 ] ++ [ 3*3]
=
[ 1 ] ++ [ 4 ] ++ [ 9 ]
=
[1, 4, 9]

E. HASKELL LIST COMPREHENSION GENERATOR AND FILTER EXAMPLE

Prelude>  let y = [ x*x | x <- [1..3], odd x ]
y :: Integral t => [t]
Prelude>

Prelude>  y
[1,9]
it :: Integral t => [t]
Prelude>

[ x*x | x <- [1..3], odd x ]
=
[ 1*1 | odd 1 ] ++ [ 2*2 | odd 2 ] ++ [ 3*3 | odd 3 ]
=
[ 1 | True] ++ [ 4 | False ] ++ [ 9 | True]
=
[ 1 ] ++ [  ] ++ [ 9 ]
=
[1,9]

F. HASKELL LIST COMPREHENSION TWO GENERATORS EXAMPLE

Prelude>  let y = [ (i,j) | i <- [1..3], j <- [i..3] ]
y :: (Num t, Enum t) => [(t, t)]
Prelude>

Prelude>  y
[(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]
it :: (Num t, Enum t) => [(t, t)]
Prelude>  

[ (i,j) | i <- [1..3], j <- [i..3] ]
=
[ (1,j) | j <- [1..3] ] ++
[ (2,j) | j <- [2..3] ] ++
[ (3,j) | j <- [3..3] ] 
=
[ (1,1), (1,2), (1,3) ] ++
[ (2,2), (2,3) ] ++
[ (3,3) ] 
=
[ (1,1), (1,2), (1,3), (2,2), (2,3), (3,3) ]

G. HASKELL LIST COMPREHENSION TWO GENERATORS AND FILTER EXAMPLE

Prelude>  let y = [(i,j) | i <- [1..3], j <- [1..3], i <= j]
y :: (Ord t, Num t, Enum t) => [(t, t)]
Prelude>

Prelude>  y
[(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]
it :: (Ord t, Num t, Enum t) => [(t, t)]
Prelude>  

[(i,j) | i <- [1..3], j <- [1..3], i <= j]
=
[ (1,j) | j <- [1..3], 1 <= j ] ++
[ (2,j) | j <- [1..3], 2 <= j ] ++
[ (3,j) | j <- [1..3], 3 <= j ]
=
[(1,1)|1<=1] ++ [(1,2)|1<=2] ++ [(1,3)|1<=3] ++
[(2,1)|2<=1] ++ [(2,2)|2<=2] ++ [(2,3)|2<=3] ++
[(3,1)|3<=1] ++ [(3,2)|3<=2] ++ [(3,3)|3<=3]
=
[(1,1)]      ++ [(1,2)]      ++ [(1,3)]      ++
[]              ++ [(2,2)]      ++ [(2,3)]      ++
[]              ++ []              ++ [(3,3)]
=
[(1,1),(1,2),(1,3),(2,2),(2,3),(3,3)]

H. LIST COMPREHENSION VERSUS RECURSION

-- Comprehension
squaresComp ::[Integer] -> [Integer]
squaresComp xs = [ x*x | x <- xs ]

-- Recursion
squaresRec :: [Integer] -> [Integer]
squaresRec []        = []
squaresRec (x:xs) = x*x : squaresRec xs

II. CONCLUSION

In this tutorial, you have been introduced to Haskell list comprehension.

III. 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.



Saturday, February 2, 2013

HASKELL LISTS

HASKELL LISTS


REVISED: Saturday, February 3, 2024




Haskell Lists.

I.  HASKELL NUMERIC AND CHARACTER LISTS

The Data.List module is the home of all standard list functions.

Prelude>  :module +Data.List
Prelude>

Prelude>  "U" `isPrefixOf` "USA" -- isPrefixOf function matches at the beginning of a list.
True
it :: Bool
Prelude>

Prelude>  "S" `isInfixOf` "USA"   -- isInfixOf function matches anywhere in a list.
True
it :: Bool
Prelude>

Prelude>  "A" `isSuffixOf` "USA" -- isSuffixOf function matches at the end of a list.
True
it :: Bool
Prelude>

Prelude>  :type isPrefixOf
isPrefixOf :: Eq a => [a] -> [a] -> Bool
Prelude>

Prelude>  :type isInfixOf
isInfixOf :: Eq a => [a] -> [a] -> Bool
Prelude>

Prelude>  :type isSuffixOf
isSuffixOf :: Eq a => [a] -> [a] -> Bool
Prelude>    

A list can be of any length.

In Haskell, lists are what arrays are in most other languages. 

[]  pronounced "nil" is an empty list.

Prelude> :type []
[] :: [t]
Prelude>

Haskell uses pattern matching to tell the compiler how to process data.

The module shown below has two patterns for the compiler to choose from. The first pattern is myMod [] and the second pattern is myMod (x:xs). The compiler makes a choice between the two patterns based on the contents of the function myMod argument. If the argument is an empty list [] the compiler chooses the myMod [] pattern. If the argument is a singleton list [x] or a list of [xs] the compiler chooses the myMod (x:xs) pattern. By convention, list arguments usually have an s suffix on their name; e.g., [xs] for a list of xs, or  [[xss],[]] for a list of lists and so forth.

The function syntax is as follows:

<function name> <function arguments> = <return values>

error is a Prelude function that allows the programmer to code a message to be printed when an exception error occurs. In the module shown below the programmer is telling the compiler that when myMod has the empty list [] as an argument to print the exception error message "Empty list!". The programmer can choose any message to be printed as long as the message in enclosed in double " " marks.

Module:

module MyMod where  -- All module names; i.e., MyMod start with a capital letter.

myMod :: [t] -> [t]  -- Function signature.
myMod [] = error "Empty list!"  -- When argument is an empty list [], prints exception error message.
myMod (x:xs) = xs  -- Pattern which returns xs.

GHCi:

Prelude>  :load MyMod
[1 of 1] Compiling MyMod               ( MyMod.hs, interpreted )
Ok, modules loaded: MyMod.
Prelude>

Prelude>  myMod []
*** Exception: Empty list!
Prelude> 

Prelude>  myMod [1,2,3,4,5,6,7,8,9,10]
[2,3,4,5,6,7,8,9,10] -- Notice the compiler return xs which is the tail of the list.
Prelude>  

A singleton pattern is a design pattern that restricts the instantiation of a class to one object. 

[a] is a singleton list (a:[]).

Prelude>  :t (:[])  -- Rotate (:[]) 90 degrees to the right and you see the head of a robot.
(:[]) :: a -> [a]
Prelude>  

[l, 2]  is read, "list with elements of type integer."

Prelude> :type [1,2]
[1,2] :: Num t => [t]
Prelude>

=> reads, "implies". The way to read the above is to say, “t being an instance of Num implies a list [t] of t's.

[True,False]  is read, "a list with elements of type Boolean."

Prelude> :type [True,False]
[True,False] :: [Bool]
Prelude>

[(1, 2), (2, 3)]  is read, "a list of tuples of Integers".

Prelude> :type [(1,2),(2,3)]
[(1,2),(2,3)] :: (Num t1, Num t) => [(t, t1)]
Prelude>

[[1, 2], [2, 3, 4]]  is read, "a list of Integer-lists".

Prelude> :type [[1,2],[2,3,4]]
[[1,2],[2,3,4]] :: Num t => [[t]]
Prelude>

A. HASKELL LISTS FROM GIVEN ELEMENTS

The value to the left of the :,  pronounced "cons" for construct, is placed as the first element of the list. cons is another word for :. In lists, : is actually a constructor that takes a value and another list, and returns a list.

Prelude>  :type (:)
(:) :: a -> [a] -> [a]
Prelude>

Prelude> 1 : 2 : 3 : []
[1,2,3]
it :: Num a => [a]
Prelude>

We can also use the Prelude foldr function to show an example of the identity function. 

Prelude>  let myId = foldr (:) []
Prelude>

Prelude>  :type myId
myId :: [a] -> [a]
Prelude>

Prelude>  myId [1,2,3]
[1,2,3]
it :: Num a => [a]
Prelude>  

These examples are really singly-linked lists, and not arrays. Haskell does not have array primitives.

Prelude> 4: 5 : 6 : []
[4,5,6]
it :: Num a => [a]
Prelude>

Prelude> "1" : "2" : "3" : []
["1","2","3"]
it :: [[Char]]
Prelude>

Prelude> "4": "5": "6": []
["4","5","6"]
it :: [[Char]]
Prelude>

B. COMBINING HASKELL LISTS

Prelude> [1,2,3] ++ [4,5,6]     -- ++ is pronounced, "append"; and concatenates two lists.
[1,2,3,4,5,6]
it :: Num a => [a]
Prelude>

Prelude> ["1","2","3"] ++ ["4","5","6"]
["1","2","3","4","5","6"]
it :: [[Char]]
Prelude>

Prelude>  :type (++)   -- :t is the same as :type.
(++) :: [a] -> [a] -> [a]
Prelude>

Prelude>  concat [ ["1","2","3"] , ["4","5","6"] ]
["1","2","3","4","5","6"]   -- Prelude concat function removes one level of nesting; i.e., the inner square brackets.
it :: [[Char]]
Prelude>  

Prelude>  :type concat
concat :: [[a]] -> [a]
Prelude>

module MyApp2List where

(++)              :: [a] -> [a] -> [a]
[]        ++ ys = ys
(x:xs) ++ ys = x : (xs ++ yx)

II. ACCESSING HASKELL SUBLISTS

Strings are lists of characters; therefore, you can use list functions on strings.

A. ACCESSING THE FIRST ELEMENT OF A LIST

To access the first element of a list, we use the Prelude head function.

Prelude> :type head
head :: [a] -> a
Prelude>

a is the type variable. We can read the signature as “takes a list, all of whose elements have some type a, and returns a value of the same type a”.

Prelude> let ls = ["1","2","3","4","5","6"]  -- ls is immutable, it never changes.
Prelude>

Prelude> head ls -- Returns a new list containing the first list element of ls.
"1"
it :: [Char]
Prelude>

head is what is known as a partial function. There are certain inputs, i.e., empty list [], for which head will crash. 

Prelude>  head []
*** Exception: Prelude.head: empty list  -- Calling an exception or an error does not qualify as returning a value!
Prelude>  

Functions which have certain inputs that will make them recurse infinitely are also called partial. Functions which are well-defined on all possible inputs are known as total functions. It is good Haskell practice to avoid partial functions. Other partial Prelude functions include tail, init, last, and (!!).

Shown below is the Prelude tail function, which returns all but the head of a list.

Prelude> let ls =  ["1","2","3","4","5","6"]
Prelude>

You have to add ls to local memory because when you do the :load MyTail all local memory is erased.

Prelude> tail ls  -- Removes the first list element from list ls.
["2","3","4","5","6"]  -- Haskell creates a new list; this is not the original ls list.
it :: [[Char]]
Prelude>

Writing your own version of Prelude functions helps you learn Haskell. For example, the following myTail is a version of Prelude Tail.

Module:

module MyTail where  -- Module names; e.g., MyTail must always start with capital letter.

myTail            :: [t] -> [t]  -- Function names; e.g., myTail must always start with lower case letter.
myTail        [] = error "Empty list!"
myTail (x:xs) = xs

GHCi:

Prelude> :load MyTail
[1 of 1] Compiling MyTail               ( MyTail.hs, interpreted )
Ok, modules loaded: MyTail.
Prelude>

Prelude> let ls =  ["1","2","3","4","5","6"]
Prelude>

You have to add ls to local memory because when you do the :load MyTail all local memory is erased.

Prelude>  myTail ls
["2","3","4","5","6"]  -- The list returned by myTail function body is a copy of ls.
Prelude>  

Prelude> ls
["1","2","3","4","5","6"] -- Original ls is immutable; therefore, does not change.
Prelude>

B. ACCESSING THE LAST ELEMENT OF A LIST

Prelude function last, returns the very last element of a list.

Prelude> :type last
last :: [a] -> a
Prelude>

Prelude> last ls
"6"
it :: [Char]
Prelude>

Prelude> ls
["1","2","3","4","5","6"]  -- ls is immutable; therefore,  ls does not change.
Prelude>

Module:

module MyLast where

myLast            :: [t] -> t
myLast        [] = error "Empty list!"
myLast      [x] = x
myLast (x:xs) = myLast xs

GHCi:

Prelude>  :load MyLast
[1 of 1] Compiling MyLast               ( MyLast.hs, interpreted )
Ok, modules loaded: MyLast.
Prelude>

Prelude>  let ls = ["1","2","3","4","5","6"]
Prelude>

Prelude>  myLast ls
"6"
Prelude>

C. ACCESSING AN ELEMENT AT A GIVEN INDEX

If you want to get an element out of a list by index, use !! pronounced index.

Prelude> :type (!!)
(!!) :: [a] -> Int -> a
Prelude>

Prelude> ["1","2","3","4","5","6"] !! 3
"4"
it :: [Char]
Prelude>

There are both "zero based indexes" and "one based indexes". Zero based indexes start with zero. One based indexes start with one. Haskell uses zero based indexes for lists. Therefore, in our example above; index 0 would return 1, index 1 would return 2, index 2 would return 3, and index 3 would return 4.

D. ACCESSING THE FIRST N ELEMENTS

Prelude> :type take
take :: Int -> [a] -> [a]
Prelude>

The -> is right associative; therefore, take gives type Int to a function.

take :: Int -> ([a] -> [a])

The function take takes one argument, an Int, and returns another function ([a] -> [a]). That other function also takes one argument, a list [a] , and returns a list [a] of the same type as its result.

The take function has two arguments: an integer n, and a list ls; and produces the first n elements of ls. By convention a list is written ls and a list of lists would be written lss each list argument with a s suffix.

Prelude> take 4 ["1","2","3","4","5","6"]
["1","2","3","4"]
it :: [[Char]]
Prelude>

Prelude>  takeWhile (<= 5) [1..]
[1,2,3,4,5]
Prelude>  

Module:

module MyTake where

myTake                :: (Ord a, Num a) => a -> [t] -> [t]
myTake n _
      | n <= 0         = []                                    -- Checking for negative numbers.
myTake _ []         = []                                    -- Checking for empty list.
myTake n (x:xs)  = x:myTake (n-1) xs  -- Checking all other cases.

GHCi:

Prelude>  :load MyTake
[1 of 1] Compiling MyTake               ( MyTake.hs, interpreted )
Ok, modules loaded: MyTake.
Prelude>

Prelude>  myTake (-5) [1,2,3,4,5,6]
[]
Prelude>

Prelude>  myTake 5 []
[]
Prelude>

Prelude>  myTake 5 [1,2,3,4,5,6,7,8,9,10]
[1,2,3,4,5]
Prelude>  

E. ACCESSING THE LAST N ELEMENTS

Prelude> :type reverse
reverse :: [a] -> [a]
Prelude>

Prelude> reverse . take 4 . reverse $ ["1","2","3","4","5","6"]
["3","4","5","6"]
it :: [[Char]]
Prelude>

The dot . is the function composition operator, dot is read as, "after". The dot . operator has very high precedence, surpassed only by function application.

Function composition is the act of pipelining the result of one function, to the input of another, creating an entirely new function. This can be done with any two functions, where the argument type of the first is the return type of the second.

$ is pronounced, "apply". When a $ is encountered, the expression on its right is applied as the parameter to the function on its left.

F. ACCESSING THE N ELEMENTS STARTING FROM INDEX M

Prelude> :type drop
drop :: Int -> [a] -> [a]
Prelude>

Prelude> take 4 $ drop 1 ["1","2","3","4","5","6"]
["2","3","4","5"]
it :: [[Char]]
Prelude>

module Drop where
drop                       :: Int -> [a] -> [a]
drop 0         xs       = xs
drop (n+1) []        = []
drop (n+1) (_:xs) = drop n xs

G. SPLITTING A STRING INTO A LIST OF WORDS

Prelude> :type words
words :: String -> [String]
Prelude>

Prelude> words "Mary had a little lamb."
["Mary","had","a","little","lamb."]
it :: [String]
Prelude>

Prelude>  unwords ["Mary","had","a","little","lamb."]
"Mary had a little lamb."
it :: String
Prelude>  

H. SPLITTING A LIST INTO TWO PARTS

Prelude> :type splitAt
splitAt :: Int -> [a] -> ([a], [a])
Prelude>

Prelude> splitAt 12 "abcdefghijklmnopqrstuvwxyz"
("abcdefghijkl","mnopqrstuvwxyz")
Prelude>
Prelude> splitAt 12 ['a'..'z']
("abcdefghijkl","mnopqrstuvwxyz")
Prelude>

I. LENGTH OF A LIST

The length function tells us how many elements are in a list.

Prelude> :type length
length :: [a] -> Int
Prelude>

Prelude> length "abcdefghijklmnopqrstuvwxyz"
26
Prelude> length ['a'..'z']
26
Prelude>length [1..10]
10
Prelude>

module Length where

length            :: [a] -> Int
length        [] = 0
length (_:xs) = 1 + length xs
{-
length maps the empty list to 0, and any non-empty list to
the successor of the length of its tail.
-}

Prelude> :{
Prelude| len [] = 0

Prelude| len (_:xs) = 1 + len xs
Prelude| :}
Prelude> len [1,2,3,4,5,6,7,8,9,10]

10
Prelude>


Prelude> let myLength = sum . map (\_ -> 1)
Prelude> myLength [1,2,3,4]
4

J. REVERSE A LIST

Prelude> :type reverse
reverse :: [a] -> [a]
Prelude>

Prelude> reverse [1,2,3]
[3,2,1]
it :: Num a => [a]
Prelude>

module Reverse where

reverse            :: [a] -> [a]
reverse        [] = []
reverse (x:xs) = reverse xs ++ [x]
{-
reverse maps the empty list to the empty list
and any non-empty list to the reverse of its tail
appended to its head.
-}

The process of interpreting code by substituting equals-for-equals is known as equational reasoning. For example, with equational reasoning we can compute the reverse of 1, 2, 3 as follows:

reverse [1,2,3]
=
reverse [2,3] ++ [1]
=
(reverse [3] ++ [2]) ++ [1]
=
((reverse [] ++ [3]) ++ [2]) ++ [1]
=
(([] ++ [3]) ++ [2]) ++ [1]
=
[3,2,1]

K. APPLY A FUNCTION TO EACH ELEMENT IN A LIST

The higher-order library function called map applies a function; for example, (5*) to every element of a list.

Prelude> :type map
map :: (a -> b) -> [a] -> [b]
Prelude>

Prelude> map (5*) [1, 2, 3, 4, 5, 6]
[5, 10, 15, 20, 25, 30]
it :: Num b => [b]
Prelude>

Using (5*) is called sectioning notation which is the same as using an anonymous function fiveX = \x -> x * 5.

Prelude>  let fiveX = \x -> x * 5
Prelude>

Prelude>  fiveX 2
10
Prelude>

Prelude>  :type fiveX
fiveX :: Num a => a -> a
Prelude>  

The map function defined using list comprehension:

map f xs = [f x | x <- xs]

The map function defined using recursion:

map f []        = []
map f (x:xs) = f x : map f xs

L. FILTER A LIST

Prelude> :type filter
filter :: (a -> Bool) -> [a] -> [a]
Prelude>

filter cannot add elements; it can only remove them. When we filter we are choosing whether to keep an element of a list, depending on whether or not a particular predicate holds. A predicate is a function of type Int -> Bool which returns True for those elements which should be kept, and False for those which should be discarded.

filter returns a list constructed from members of a list, the second argument, fulfilling a condition given by the predicate, the first argument.

Prelude> filter (>5) [1, 2, 3, 4, 5, 6, 7, 8]
[6, 7, 8]
it :: (Ord a, Num a) => [a]
Prelude>

Filter defined using list comprehension:

filter p xs = [x | x <- xs, p x]

Filter defined using recursion:

filter p []        = []
filter p (x:xs)
    | p x            = x : filter p xs
    | otherwise = filter p xs

M. FOLD A LIST

Any function that traverse a list from left to right or right to left, one element by element, and accumulates a result, can be implemented with a fold.

Prelude> :type foldl
foldl :: (b -> a -> b) -> b -> [a] -> b
Prelude>

foldl takes a function which turns a b and a b into a b, an initial value of type b and a list of as. It produces a b.

Prelude> :type foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
Prelude>

fold takes in a function and folds it in between the elements of a list. foldr folds from the right and foldl folds from the left.

sum       = foldr (+)   0

product = foldr (*)    1

or          = foldr (||)   False

and        = foldr (&&) True

Foldr defined using recursion and the above notation:

f = (+)

v = 0

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v []        = v
foldr f v (x:xs) = f x (foldr f v xs)

The compiler is able to turn most recursions into loops. This is called tail recursion optimization, where the recursive call at the very end of a function is simply turned into a goto to the beginning of the function. 

Prelude> import Data.Foldable
Prelude>

Prelude> Data.Foldable.foldl (+) 0 [1..5]     -- Zero indexed, 0 starting point.
15
it :: (Num a, Enum a) => a
Prelude>

This is the same as:

0 + 1 + 2 + 3 + 4 + 5

which equals 15.

This is the same as:

sum2            :: Num a => [a] -> a
sum2 []        = 0
sum2 (x:xs) = x + sum2 xs

Prelude>  :load sum2
[1 of 1] Compiling Main             ( sum2.hs, interpreted )
Ok, modules loaded: Main.
Prelude>

Prelude>  sum2 [0,1,2,3,4,5]
15
Prelude>

sum2 [0,1,2,3,4,5]
=
foldr (+) 0 [0,1,2,3,4,5]
=
foldr (+) 0 (0:(1:(2:(3:(4:(5:[]))))))  -- fold right
=
0+(1+(2+(3+(4+(5+0)))))
=
6

N. ZIP A LIST

As shown below zip transposes a pair of lists ([a] , [b]) into a list of pairs [(a, b)] :

Prelude> :type zip
zip :: [a] -> [b] -> [(a, b)]
Prelude>

The library function zip produces a new list by pairing successive elements from two existing lists until either or both are exhausted.

Prelude> zip [1,2,3] [9,8,7]
[(1,9),(2,8),(3,7)]
it :: (Num b, Num a) => [(a, b)]
Prelude>

Module:

module MyZip where

myZip                       :: [a] -> [b] -> [(a,b)]     -- Function signature.
myZip  []  _              = []                                           -- [a] becomes empty first.
myZip  _   []             = []                                           -- [b] becomes empty first.
myZip (x:xs) (y:ys) = (x,y) : myZip xs ys             

GHCi:

Prelude>  :load MyZip
[1 of 1] Compiling MyZip               ( MyZip.hs, interpreted )
Ok, modules loaded: MyZip.
Prelude>

Prelude>  let ls1 = ["1", "2", "3"]
Prelude>

Prelude>  let ls2 = ["4", "5", "6"]
Prelude>

Prelude>  myZip ls1 ls2
[("1","4"),("2","5"),("3","6")]
Prelude>  

O. ZIPWITH A LIST

Prelude> :type zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
Prelude>

zipWith makes a list whose elements are calculated from a function and the elements of input lists occurring at the same position in both lists.

Prelude> zipWith (+) [1,2,3] [3,2,1]
[4,4,4]
it :: Num c => [c]
Prelude>

P. liftM

Prelude>  import Control.Monad
Prelude>

liftM promotes a function to a monad.

Prelude> :type liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
Prelude>

Prelude> import Control.Monad
Prelude>

"Copy Paste" the following script into your text editor, and "File, Save As" MaryTxtFile.txt in your working directory.

Mary had a little lamb,
its fleas were white as snow.
And everywhere that Mary went,
its fleas were sure to go.

Prelude>  :cd C:\Users\Tinnel\Haskell2024\
Prelude>

Prelude> liftM lines (readFile "MaryTxtFile.txt")
["Mary had a little lamb,","its fleas were white as snow.","And everywhere that Mary went,","its fleas were sure to go."]
it :: [String]
Prelude>

Q. LIST COMPARISONS

Lists can be compared if the elements within the lists can be compared. When using <, <=, >, and >= to compare lists, they are compared in lexicographical order. First the heads are compared. If they are equal then the second elements are compared, etc.

R. LIST COMPREHENSIONS

Let S be a set,   be for every,  be is an element of, and p(x) be a predicate.

Mathematically we can write the following statement.

∀x∈S, p(x)


For every x such that x is an element of the set S and satisfies the predicate p(x).

We write the above in Haskell as follows:

[ x | x <- S,  p x ]

In Haskell <- is pronounced "drawn from" and p x is called a condition, or "predicate".

Below, the function x which satisfies the predicate x+5  is drawn from the set [1,2,3].

Prelude>  [ x+5 | x <- [1,2,3] ]
[6,7,8]
it :: Num t => [t]
Prelude>

Prelude>  [ x | x <- [2..10], 10 `mod` x  == 0]
[2,5,10]
Prelude>

Prelude>  [ team ++ " " ++ player | 
             team   <- ["red", "blue"], 
             player <- ["soldier", "pyro", "scout"] ]  -- Surprised? Try it.
["red soldier","red pyro","red scout","blue soldier","blue pyro","blue scout"]
it :: [[Char]]
Prelude> 

The following is accepted by Prelude as shown above because it is one single line of code:

[ team ++ " " ++ player | team   <- ["red", "blue"], player <- ["soldier", "pyro", "scout"] ] 

List comprehensions are used for building more specific lists out of general lists.

S is a set.
is an element of.
is a set of all natural numbers.

S = { x * 3 | x ∈ ℕ, (x * 3) ≦ 51 }

Prelude>  [x*3 | x <- [0..], x*3 <= 51]  
[0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51Interrupted.
Prelude>

In the example above GHCi will stop processing at 51. There is a tool bar at the top of the GHCi window. Select Actions in the GHCi tool bar, and then select Stop; or press Ctrl+C to interrupt processing and to return to the Prelude> prompt.

S = { x * 3 | x ∈ [12..20], (x * 3) ≦ 51 }

Prelude> [x*3 | x <- [12..20], x*3 <= 51]  
[36,39,42,45,48,51]
it :: (Ord t, Num t, Enum t) => [t]
Prelude>

The part before the pipe |, is the output function x*3. x is drawn from the input set of 12 to 20. For every element in 12..20 that we have bound to x we get that element x multiplied by 3 if it satisfies the condition, predicate, of being <= 51.

List comprehensions are an expressive shorthand for building computations on lists.

S. LIST DIFFERENCE \\ OPERATOR

Prelude> :type (\\)
(\\) :: Eq a => [a] -> [a] -> [a]
Prelude>

The list difference \\ operator takes two lists, goes through the second and for each item, removes the first instance of the same item from the first list.

Prelude> import Data.List
Prelude>

Prelude> [1..10] \\ [2, 3, 5, 8]
[1,4,6,7,9,10]
it :: (Num a, Eq a, Enum a) => [a]
Prelude>

[12,3,4,5,6,7,8,9,10]        -- First list [1..10].
[    2,3,    5,       8           ]       -- Second list.
[1,      4,   6,7,   9,10]

T. NUB

nubeliminates all duplicate elements from a list.

Prelude>  :type nub
nub :: Eq a => [a] -> [a]

Prelude>

Prelude>  import Data.List
Prelude>

Prelude>  nub [1,1,2,2,3,3,4,4,5,5]
[1,2,3,4,5]
Prelude>

U. INIT

init, returns a list of all but the last element of its input.

Prelude>  :type init
init :: [a] -> [a]
Prelude>

Prelude>  :info init
init :: [a] -> [a] -- Defined in `GHC.List'
Prelude>

Prelude>  init "hello"
"hell"
it :: [Char]
Prelude>  

Module:

module MyInit where

myInit            :: [t] -> [t]
myInit        [] = error "Empty list!"
myInit      [_] = []
myInit (x:xs) = x:myInit xs

GHCi:

Prelude>  :load MyInit
[1 of 1] Compiling MyInit              ( MyInit.hs, interpreted )
Ok, modules loaded: MyInit.

Prelude>  let ls = ["1","2","3","4","5","6"]
Prelude>

Prelude>  myInit ls
["1","2","3","4","5"]
Prelude>

Prelude>  myInit ["1"]
[]
Prelude>

Prelude>  myInit []
*** Exception: Empty list!
Prelude>

V. ELEM

elem is the list membership predicate, usually written in infix form, e.g., x `elem` xs.

Prelude>  :type elem
elem :: Eq a => a -> [a] -> Bool
Prelude>  

Prelude>  5 `elem`  [1,2,3,4,5]  -- Infix uses backticks ` not apostrophes '.
True
Prelude>

Module:

module MyElem where

myElem               :: Eq a => a -> [a] -> Bool
myElem _ []        = False
myElem e (x:xs)
        | e == x      = True
| otherwise = myElem e xs

GHCi:

Prelude>  :load MyElem
[1 of 1] Compiling MyElem               ( MyElem.hs, interpreted )
Ok, modules loaded: MyElem.
Prelude>

Prelude>  myElem 5 [1,2,3,4,5]
True
Prelude>

Prelude>  myElem 5 [1,2,3,4]
False
Prelude>

W. SORT

sort sorts a list.

Prelude>  :type sort

sort :: Ord a => [a] -> [a]

Prelude>

Prelude>  sort [4,3,6,9,1,2]

[1,2,3,4,6,9]
Prelude>

X. PRODUCT

Prelude> product [4,3,6,9,1,2]
1296
Prelude>

III. CONCLUSION

In this tutorial, you have received an introduction to Haskell lists.

IV. 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.