Download as pdf or txt
Download as pdf or txt
You are on page 1of 134

Week 2

§ Introduction
§ Characteristics
§ Environment Setup

§ Interactive Modes
§ Basic Arithmetic Manipulation

§ Datatypes
§ Haskell is a general-purpose, statically-typed, purely functional programming
language with type inference and lazy evaluation.
§ Designed for teaching, research and industrial applications, Haskell has pioneered
a number of programming language features such as type classes, which
enable type-safe operator overloading.
§ Haskell's main implementation is the Glasgow Haskell Compiler (GHC). It is named
after logician Haskell Curry.
§ Haskell is a widely used purely functional language.
§ Functional programming is based on mathematical functions.
§ Besides Haskell, some of the other popular languages that follow Functional
Programming paradigm include:
§ Lisp,
§ Python,
§ Erlang,
§ Racket,
§ F#,
§ Clojure, etc.

§ Haskell is more intelligent than other popular programming languages such as


Java, C, C++, PHP, etc.
§ One of the most common patterns in popular programming is runtime
polymorphism.
§ Haskell98 doesn’t directly support runtime polymorphism.

§ You have to use an extension called Existential-Quantification to get it.


§ This concept is much more complicated to understand in Haskell than it is to
understand as other forms such as Interfaces or Protocols or virtual methods, or
even C-structure jump tables.
§ Also, the fact that this extremely common programming pattern is not present in
the base language tells you how far Haskell’s design is from mainstream
programming needs.
§ To download Haskell
§ https://www.haskell.org/downloads/

§ Go to recommended Install Instructions


§ Copy the code on CMD (terminal)
§ Haskell can be used in two different modes
§ Interactive Mode
§ Script Mode
§ Open your terminal (cmd) and type in ghci.
§ Type ghci and enter
§ The prompt here is Prelude>
§ but because it can get longer when you load stuff into the session, we can
use ghci>.
§ To set it to ghci
§ Write :set prompt "ghci” in the terminal
§ We can also use several operators on one line and all the usual precedence rules
are obeyed.
§ We can use parentheses to make the precedence explicit or to change it
§ If we want to have a negative number, it's always best to surround it with
parentheses.
§ Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine.
§ It is also pretty straightforward.
§ && means a boolean and,
§ || means a boolean or.
§ not negates a True or a False.
§ Like numbers, Haskell can intelligently identify a character given in as an input to
it.
§ Go to Haskell command prompt and type any character with double or single
quotes
§ :t is used to check the data type of any of the input provided
§ By the error message
"<interactive>:4:1: Not in scope:
`a'" the Haskell compiler is
warning us that it is not able to
recognize your input.
§ Haskell is a type of language
where everything is represented
using a number.
§ Haskell follows conventional ASCII encoding style. Let us take a look at the
following example to understand more −

ASCII values for (A-Z and a-z) http://sticksandstones.kstrom.com/appen.html


§ A string is nothing but a collection of characters.
§ There is no specific syntax for using string, but Haskell follows the conventional
style of representing a string with double quotation.
§ Take a look at the following example where we are passing the string “Hello
World”.
§ Strings are in "double quotes.
§ It can be concatenated using ++.
§ Like other data types, List is also a very useful data type used in Haskell.
§ By definition, List is a collection of same data type separated by comma.
§ Like other data types, you need not declare a List as a List.

§ Haskell is intelligent enough to decode your input by looking at the syntax used in
the expression.
§ Take a look at the following example which shows how Haskell treats a List.
§ Lists in Haskell are homogeneous in nature, which means they won’t allow you to
declare a list of different kind of data type.
§ Haskell uses type inference to assign the most logical data type for a given
operation.
§ As a result, we don’t have to declare types if it is “obvious” such as Int vs. Double
values.

§ To explicitly declare the data types, you can add designations after each value like
so

§ 3::Int, 6.4::Float
§ Haskell doesn’t support cross-type operations, meaning have to convert values.
§ Prelude includes type conversions from different common types, such as
fromIntegral or fromDouble.

Without Type Conversion


With Type Conversion
§ by applying functions to values, we can compute new values
§ Example
§ Write a function that increments a number by the value 1;
§ let us call this function inc.
§ So, the application inc 10 should produce 11,
§ inc 11 should produce 12, and so forth —
§ in other words, for any number x, the expression inc x, should yield x + 1.
§ This general rule is formalised by the following function definition:
§ The function definition comprises a head and a body separated by an equals sign.
§ The head consists of the name of the function as well as names for the arguments to
the function.
§ In example, there is only one argument denoted by the x. When inc is applied to an
argument value, the result of the application is computed by replacing all
occurrences of the variable x in the function body by the argument value

The arrow “ ⇒ ” represents a step in progressing from an


expression to the value denoted by that expression. This
process is called expression evaluation and corresponds to the
execution of a program
§ Haskell includes predefined functions for common numerical operations like
exponents, integer division, and type conversion.
§ Power (^): Raises the first number to the power of the second number. This
executes several hidden multiplication operations and returns the final result.
§ Integer Division (div): Used to complete division on integers without changing to a
double. All decimals are truncated.
§ Modulus operator (mod) that lets you find the remainder.
The succ function takes anything that has a defined successor and returns that successor.

It separates the function name from the parameter with a space.


§ Prelude> truncate 6.59
§ 6
§ Prelude> round 6.59
§ 7
§ Prelude> sqrt 2
§ 1.4142135623730951
§ Prelude> not (5 < 3)
§ True
§ Prelude> gcd 21 14
§ 7
§ The putStr and putStrLn functions output strings to the terminal.
§ The print function outputs any type of value. (If you print a string, it will have quotes
around it.)
§ If we wanted to get the successor of the product of numbers 9 and 10, we couldn't
write succ 9 * 10 because that would get the successor of 9, which would then be
multiplied by 10. So 100.
§ We'd have to write succ (9 * 10) to get 91.
§ Print “I am learning Haskell”
§ Print “ Thompson River University” with stars
§ To find square of a number

§ To find cube of a number


§ Open any text editor
§ Go to fileàNew àStart writing code
§ Save it with .hs extension
§ Open cmd
§ Navigate to the folder where it is saved through ghci
§ Compile the program--à :l add.hs
§ The functions are called by writing the function name, a space and then the
parameters, separated by spaces.
§ Example : func_name 3
§ To call any function, use the name of function and parameter by separating them
with a space
§ Example- for add.hs , the function call will be like

add 5
Both are separated by space

Function name parameter


Functions

Output
§ Pattern Matching
§ Guards
§ Where

§ Let
§ Case
§ Pattern matching consists of specifying patterns to which some data should
conform and then checking to see if it does.
§ Define separate function bodies for different patterns.

§ Pattern matching can be performed with data type — numbers, characters, lists,
tuples, etc.
§ Call lucky,
§ The patterns will be
checked from top to
bottom and when it
conforms to a pattern, the
corresponding function
body will be used.
§ The only way a number can
conform to the first pattern
here is if it is 5.
§ If it's not, it falls through to
the second pattern, which
matches anything and
binds it to x.
1. Checking if the number is not in
between 1 and 5

2. Last pattern is known as (the


catch-all ) and checks from the
top, it would always say "Not
between 1 and 5",
§ Whereas patterns are a way of making sure a value conforms to some form and
deconstructing it,
§ guards are a way of testing whether some property of a value (or several of them)
are true or false.
§ Guards are indicated by pipes that follow a function's name and its parameters.
§ Usually, they're indented a bit to the right and lined up.
§ A guard is basically a boolean expression.

§ If it evaluates to True, then the corresponding function body is used.


§ If it evaluates to False, checking drops through to the next guard and so on.
§ findMax a b
§ |a>b = "a is greater"
§ |b>a = "b is greater"
§ Many times, the last guard is otherwise.
§ otherwise is defined simply as otherwise = True and catches everything.
§ Let bindings let you bind to variables anywhere and are expressions themselves,
but are very local, so they don't span across guards.
§ The form is let <bindings> in <expression>. The names that you define in
the let part are accessible to the expression after the in part.
§ let puts the bindings first and the expression that uses them later whereas where is
the other way around.
§ The difference is that let bindings are expressions themselves. where bindings are
just syntactic constructs.
§ The names defined in the where section of a function are only visible to that
function.
§ Where bindings bind to variables at the end of a function and the whole function
can see them, including all the guards.
§ CASE can evaluate expressions based on the possible cases of the value of a
variable, we can also do pattern matching.
§ aaa x = case x of
§ 1 -> "A"
§ 2 -> "B"
§ 3 -> "C"
§ xs -> "nothing"
§ Recursion
§ Recursion is actually a way of defining functions in which the function is applied
inside its own definition.

§ Why recursion is important?

§ In Haskell, computations are done by declaring what something is instead of


declaring how you get it.
§ That's why there are no while loops or for loops in Haskell and instead we many
times have to use recursion to declare what something is.
§ Having an element or two in a recursion definition defined non-recursively is also
called the edge condition
§ It is important if you want your recursive function to terminate.

§ Example-
§ In factorial, f(0)=1, f(1)=1
§ factorial :: Int->Int
§ factorial 0 =1
§ factorial n= n * factorial (n-1)
In quicksort, an element that you compare
against is called a pivot. They're
The elements that are smaller than the pivot are light
in green here.
green and elements larger than the pivot are dark green.

head because it's easy to get by pattern matching.


§ Define an edge case and
§ then define a function that does something between some element and the function
applied to the rest.
§ Often the edge case value turns out to be an identity.
§ The identity for multiplication is 1 because if you multiply something by 1, you get
that something back.
§ Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the
identity for addition.
§ In quicksort, the edge case is the empty list and the identity is also the empty list,
because if you add an empty list to a list, you just get the original list back.
§ To solve a problem,
§ Try to think of when a recursive solution doesn't apply and see if you can use that
as an edge case,
§ think about identities and
§ think about whether you'll break apart the parameters of the function (for instance,
lists are usually broken into a head and a tail via pattern matching) and on which
part you'll use the recursive call.
§ Functions
§ User Input
§ Curried functions
§ 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.
§ 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
§ Main– main function
§ Do– do will create a chain of different commands and would store it in main
§ printStrLn- to print any string
§ Quit ghci by typing :q
§ Write:
§ ghc –make function.hs
§ Here Haskell compiler will compile the program or (Haskell file .hs) and create number of
files
§ Files-
§ Function.o
§ Function.hi
§ Function.exe
§ Call the script file using the name of it
§ Example- functions.hs
§ The .o is exactly the same as C's object files;
§ the .hi file is an "interface file"; it contains information about the .o that GHC would
need, if you compile other modules, to be able to link against that .o file (said
information cannot be stored in a standard .o file).

§ You could say that the .hi file is the equivalent of C's header files (that is, with .h
extension), only these are generated by GHC from the original Haskell source.

§ Thus, the .hi is used when GHC compiles other modules, and the .o is used when
linking all modules together to produce the executable.

§ You can safely delete the .hi and .o files once you have successfully generated the
executable (or keep them if you want to make some small change and rebuild quickly -
it will save time in unneeded recompilations).
§ To run a file - .\functions
§ If using .\functions.hs
§ It will open a .hs file
§ main = do --do keyword will create a chain of commands for you
§ putStrLn "What is your name?"
§ name <-getLine
§ putStrLn ("hello my name is " ++ name)
§ Every function in Haskell officially only takes one parameter.
§ All the functions that accepted several parameters so far have been curried
functions.

§ A curried function is a function of several arguments rewritten such that it


accepts the first argument and returns a function that accepts the second
argument and so on.

§ This allows functions of several arguments to have some of their initial arguments
partially applied.
§ max (a b)
§ Example- max 4 5
§ Firstly function max takes 4 as an argument and later returns a function that will
process maximum of 4 5
§ https://www.youtube.com/watch?v=psmu_VAuiag
Thompson River University
§ Currying
§ Partial function
§ Every function in Haskell officially only takes one parameter.
§ All the functions that accepted several parameters so far have been curried
functions.

§ A curried function is a function of several arguments rewritten such that it


accepts the first argument and returns a function that accepts the second
argument and so on.

§ This allows functions of several arguments to have some of their initial arguments
partially applied.
§ max (a b)
§ Example- max 4 5
§ Firstly function max takes 4 as an argument and later returns a function that will
process maximum of 4 5

Calling a function with too few parameters, we get back


a partially applied function, meaning a function that takes
as many parameters as we left out.
§ A partial function is a function that is not defined for all possible arguments of the
specified type.

§ Examples in the Haskell standard library are:


§ head, tail:
§ undefined for empty lists

§ div:
§ undefined if the divisor is zero
§ Sometimes called Partial Functions
§ Partial application in Haskell involves passing less than the full number of
arguments to a function that takes multiple arguments.

§ What happens when a function only gets some arguments?

§ Prelude> add x = x + 3

§ Prelude> p3 = add 3

§
What is p3?
§ Converting a function with multiple arguments into a series of one argument
functions

§ f(x, y, z) = ( ( g(x) )(y) )(z)

= g(x)(y)(z)

§ Named after logician Haskell Curry (1900-1982)


§ https://www.youtube.com/watch?v=psmu_VAuiag
§ In the Haskell programming language, all functions are curried by default (thus the
lack of parentheses)

§ But: we convert between curried and uncurried versions:

§ mod 1 2 == uncurry (mod) (1,2)


§ fst (1,2) == curry fst 1 2
§ Lambda calculus is a theoretical model of computation
§ Designed by mathematician Alonzo Church in 1930s
§ Universal computation ≡ Turing Machine
§ Uses Greek letter λ (lambda) to show inputs/arguments
§ λx.x +y is equivalent to f(x) = x + y

§ Lambda notation is used in Haskell (and other languages) to define anonymous


functions
§ \x -> 2 * x
§ \x y -> x + y
§ An anonymous function is a function without a name.
§ It is a Lambda abstraction and might look like this: \x -> x + 1.
§ That backslash is Haskell's way of expressing a λ and is supposed to look like a
Lambda.
§ Lambda calculus has applications in many different areas
in mathematics, philosophy,linguistics, and computer science.
§ Lambda calculus has played an important role in the development of the theory of
programming languages.
§ Functional programming languages implement lambda calculus.
§ https://www.youtube.com/watch?v=eis11j_iGMs
§ High order functions are those which can take functions as parameters

§ Example-

§ map function
§ filter
§ map takes a function and a list and applies that function to every element in the list,
producing a new list.
filter Filter is a function that takes a predicate and a list and then returns
the list of elements that satisfy the predicate.

A predicate is a function that tells whether something is true or not (function


that returns a boolean value)
Can perform all this using

list comprehensions

Example-
map (+3) [1,5,3,1,6] is the same as writing [x+3 | x <- [1,5,3,1,6]]

Do not have any rule


§ It creates a list from another one,
§ it inspects the original list and takes from it its elements to the moment when the
condition fails, then it stops processing
§ Example—

§ Input: takeWhile (<3) [1,2,3,4,5]


§ Output: [1,2] ------ condition fails at 3

§ Input: takeWhile (>3) [1,2,3,4,5]


§ Output: [] ----------- condition fails at first element, so empty
list
§ A fold takes a binary function, a starting value (accumulator) and a list to fold up.
§ The binary function itself takes two parameters.
§ The binary function is called with the accumulator and the first (or last) element
and produces a new accumulator.
§ Foldl function, also called the left fold.
§ It folds the list up from the left side.
§ The binary function is applied between the starting value and the head of the list.

§ That produces a new accumulator value and the binary function is called with that
value and the next element, etc.
§ The right fold, foldr works in a similar way to the left fold, only the accumulator eats
up the values from the right.

§ The foldl1 and foldr1 functions work much like foldl and foldr, only you don't
need to provide them with an explicit starting value. They assume the first (or last)
element of the list to be the starting value and then start the fold with the element
next to it.
§ scanl and scanr are like foldl and foldr, only they report all the intermediate
accumulator states in the form of a list.

When using a scanl, the final result will be in the last


element of the resulting list while a scanr will place the
result in the head.
§ Application operator
§ Function Composition
§ Composition Operator
§ $ sign as a replacement for parenthesis
§ For example, the following expression:

take 1 $ filter even [1..10] Output: [2]

if we don't put the $


take 1 filter even [1..10]

the compiler would now complain, because it would think we're trying to apply 4
arguments to the take function,
take 1 filter even [1..10]

§ 4 arguments
§ 1 :: Int,
§ filter :: (a -> Bool) -> [a] -> [a],
§ even :: Integral a => a -> Bool,
§ [1..10] :: [Int].

§ To avoid this, put parenthesis

§ Example-

(take 1) (filter even [1..10])


(take 1) (filter even [1..10]) take 1 $ filter even [1..10]

(take 1) ([2,4,6,8,10])

take 1 [2,4,6,8,10]

[2]
Type Signature of take:

Int -> [a] -> [a]

Syntax: take param1 [param2]

Param1: This param will always return us the same number of elements that we pass here

Param 2: List

Example----- take 3 [1, 2, 2 ]


§ Input: take 5 [1,2]
§ Output: [1,2]

§ Input: take 0 [1,2,3,4,5,6,7]


§ Output: []
§ Idea is based on mathematics
§ In mathematics,
§ f(x) and g(x),

§ Can be composed or written as


§ f(g(x)).

§ The expression f(g(x)) first calls g and then calls f.


§ new function, h, can be generated by combining these two functions
§ h=f∘g, i.e. h is the composition of f and g
f g h
§ Functions can be chained into each other, e.g.:
§ 𝑓 𝑥 = 2𝑥 ! ≡ 𝑔 ∘ ℎ 𝑥
§ 𝑤ℎ𝑒𝑟𝑒 𝑔 𝑥 = 2𝑥 𝑎𝑛𝑑 ℎ 𝑥 = 𝑥 !

The result of one function directly feeds into the other

The two functions merge in a composition


In Haskell we denote this with periods:
f x = (g.h) x
where g x = (*) 2 x
h x = (^) x 2
§ Functions do not necessarily need names

§ They might be the return value of another function

§ Or something used in the middle of a block of code

§ But: if anonymous, they can’t be referred to within the code


§ There is no name to use!

You might also like