0% found this document useful (0 votes)
19 views

Functional Programming 2.2

Lambda calculus provided a formal model of computation in 1932-33. Lisp in 1960 used symbolic computations with lists to enable functional programming. Scheme in the 1970s and ML in 1979 further developed the functional programming paradigm using features like recursion, higher-order functions, and immutable data structures. Languages in the 1980s like Miranda and Haskell aimed for a "grand unification" of functional programming, incorporating lazy evaluation. Haskell was developed in 1988 and became a widely used purely functional programming language.

Uploaded by

Anurag Sharma
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
19 views

Functional Programming 2.2

Lambda calculus provided a formal model of computation in 1932-33. Lisp in 1960 used symbolic computations with lists to enable functional programming. Scheme in the 1970s and ML in 1979 further developed the functional programming paradigm using features like recursion, higher-order functions, and immutable data structures. Languages in the 1980s like Miranda and Haskell aimed for a "grand unification" of functional programming, incorporating lazy evaluation. Haskell was developed in 1988 and became a widely used purely functional programming language.

Uploaded by

Anurag Sharma
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 114

History

4.1

Lambda Calculus
formal model of computation
(Church, 1932-33)
Lisp
(McCarthy, 1960) symbolic computations with lists
Scheme, 70s
APL
algebraic programming with arrays
(Iverson, 1962)
let and where clauses
ISWIM
equational reasoning; birth of “pure” functional
(Landin, 1966)
programming ...
ML
(Edinburgh, 1979) originally meta language for theorem proving
Caml 1985, Ocaml
SASL, KRC, Miranda
lazy evaluation
(Turner, 1976-85)
Haskell
“Grand Unification” of functional languages ...
(Hudak, Wadler, et al., 1988)
Functional Programming
◻ Functional programming is a style of programming:

Imperative Programming:
Program = Data + Algorithms
OO Programming:
■ Program = Object. message (object)

Functional Programming:
Program = Functions Functions

◻ Computation is done by application of functions

2
Functional Programming Languages

◻ A functional language supports and advocates for the style of FP.


◻ Important Features:
❖ Everything is function (input->function->output)
❖ No variables or assignments ( only constant values, arguments, and
returned values. Thus no notion of state, memory location)
❖ No loops (only recursive functions)

❖ No side-effect (Referential Transparency): the value of a function depends


only on the values of its parameters. Evaluating a function with the same
parameters gets the same results. There is no state. Evaluation order or
execution path don’t matter. (random() and getchar() are not referentially
transparent.)

❖ Functions are first-class values: functions are values, can be parameters


and return values, can be composed.
3
We can use functional programming
in imperative languages
4

◻ Imperative style
int sumto(int n)
{ int i, sum = 0;
for(i = 1; i <= n; i++) sum += i;
return sum;
}

◻ Functional style:
int sumto(int n)
{ if (n <= 0) return 0;
else return sumto(n-1) + n;
}
Why does it matter, anyway?
5

The advantages of functional programming languages:


◻ Simple semantics, concise, flexible
◻ ``No’’ side effect
◻ Less bugs

It does have drawbacks:


◻ Execution efficiency
◻ More abstract and mathematical, thus more difficult to learn and use.

Even if we don’t use FP languages:


◻ Features of recursion and higher-order functions have gotten into most
programming languages.
FP? Haskell?
Functional Programming

0
Goals

► What is Functional Programming?


► Why Functional Programming? Why Haskell?
► How do I “run” Haskell?

Chapters 1 and 2 from Hutton’s book

1
What is Functional Programming?

More a style than a paradigm


► You can write “functional code” in almost


any language
What is Functional Programming?

More a style than a paradigm


► You can write “functional code” in almost any language Some

distinguishing features:

1. Recursion instead of iteration


2. Pattern matching on values
3. Expressions instead of statements
4. Functions as first-class citizens

2
C# vs. Haskell
3

int sumUpTo(int n) {
int total = 0;
for (int i = n; n > 0; i--)
total += i;
return total;
}

sumUpTo 0 = 0
sumUpTo n = n + sumUpTo (n -1)
Try it!
4

1. Open your laptop (or look at your peer’s)


2. Go to http://repl.it/languages/haskell
3. Write your definitions on the left pane
sumUpTo 0 = 0
sumUpTo n = n + sumUpTo (n -1)
4. Click Run
5. Execute your functions on the right pane
> sumUpTo 3
6
Haskell
◻ 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. In this tutorial, we will discuss the
fundamental concepts and functionalities of Haskell
using relevant examples for easy understanding.
◻ main = putStrLn "Hello World“
//addition program
◻ main = do

◻ let var1 = 2

◻ let var2 = 3

◻ putStrLn "The addition of the two numbers is:"

◻ print(var1 + var2)
◻ //range operator
◻ main :: IO()
◻ main = do print [1..10]

◻ //
Inbuilt Type Class

◻ In Haskell, every statement is considered as a


mathematical expression and the category of this
expression is called as a Type. You can say that
"Type" is the data type of the expression used at
compile time.
◻ fType :: Int -> Int ->
◻ Int fType x y = x*x + y*y
◻ main = print (fType 2 4)
//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
Pattern Matching

◻ Pattern Matching is process of matching specific


type of expressions. It is nothing but a technique
to simplify your code.
◻ 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
Higher Order Function

◻ 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.
Higher Order Function(Example)
◻ Diving deep into the example:
(1) The function “hof” has been declared and takes
another function as a parameter (“fn”).
(2) Into the “hof” body, the “fn function” is called with
an integer argument (“20”).
(3) Finally, “hof function” duplicates the “fn function” value
returned.
(4) To use the “hof function”, we call it and send the “sumTen
function”, or even send a Lamba function — in both cases the result
was 60.
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.
◻ main = do
◻ putStrLn "The successor of 4 is:"
◻ print ((\x -> x + 1) 4)
Recursion instead of iteration
5
Iteration = repeating a process a number of times

int sumUpTo(int n) { int


total = 0;
for (int i = n; n > 0; i--) total
+= i;
return total;
}

Recursion = defining something in terms of itself

sumUpTo :: Int -> Int


sumUpTo 0 = 0
sumUpTo n = n + sumUpTo (n-1)
main = print(sumUpTo 5)
Pattern matching on values
6

A function is defined by a series of equations

► The value is compared with each left side until one “fits”
► In sumUpTo, if the value is zero we return zero,
otherwise we fall to the second one

sumUpTo 0 = 0
sumUpTo n = n + sumUpTo (n -1)
Expressions instead of statements
7

What code does versus what code is

► Statements manipulate the state of the program


► Statements have an inherent order
► Variables name and store pieces of state
int sumUpTo(int n) {
int total = 0;
for (int i = n; n > 0; i--)
total += i;
return total;
}
Expressions instead of statements
8

What code does versus what code is

► Value of a whole expr. depends only on its subexpr.


► Easier to compose and reason about
► We will learn how to reason about programs

sumUpTo 3 --> 3 + sumUpTo 2


--> 3 + 2 + sumUpTo 1
--> ...
Functions as first-class citizens
◻ If any programming language has the ability to
treat functions as values, to pass them as
arguments and to return a function from
another function then it is said that programming
language has First Class Functions and the
functions are called as First Class Citizens in that
programming language.
Functions as first-class citizens
9

Function = mapping of arguments to a result

-- In the left pane


greet name = "Hello, " ++ name ++ "!"

► Functions can be parameters of another function


► Functions can be returned from functions

-- In the right pane


>map greet ["Mary", "Joe"]
["Hello, Mary!" , "Hello, Joe!" ]

map applies the function greet to each element of the list


Try it yourself!
10

Build greet with two arguments

>greet "morning" "Paul"


"Good morning, Paul!"

-- Here is the version with one argument


greet name = "Hello, " ++ name ++ "!"


Why Functional
Programming?

To create better software

11
To create better software
12

1. Short term: fewer bugs


► Types prevent the “stupid” sort
► What does True + "1" mean?
► Purity means fewer surprises when programming
► A function can no longer mutate a global state
► Purity makes it easier to reason about programs
► Reasoning about OO =⇒ master/PhD course
► Reasoning about FP =⇒ this course
To create better software
13

2. Long term: more maintainable


► Higher-order functions remove lots of boilerplate
► Also, less code to test and fewer edge cases
► Types are always updated documentation
► Types help a lot in refactoring
► Change a definition, fix everywhere the compiler tells you
there is a problem
FP is gaining traction
14

► F# for .NET, Scala and Kotlin for JVM, Swift for iOS
► C# and Java are getting functional constructs

string greet(string name) {


return "Hello, " + name + "!"
}

var names = new List() { "Mary", "Joe" }


names.Map(x => greet(x))

► Haskell has a growing user base and nice ecosystem


► You can do webdev in Haskell!
Why Haskell?
15

Haskell can be defined with four adjectives

► Functional
► Statically typed
► Pure
► Lazy
Haskell is statically typed
16

► Every expression and function has a type


► The compiler prevents wrong combinations

> :t (+) -- Give me the type of +


Int -> Int -> Int
> 1 + 2
3
> 1 + True
Couldn't match expected type ‘Int’
with actual type ‘Bool’

Inference = if no type is given for an expression, the


compiler guesses one
Haskell is pure
17

► You cannot use statement-based programming


► Variables do not change, only give names
► Program is easy to compose, understand and paralellize
► Functions which interact with the “outer world” are
marked in their type with IO
► This prevents unintended side-effects

readFile :: FilePath -> IO ()


Why Haskell?
19

From a pedagogical standpoint


► Haskell forces a functional style
► In contrast with imperative and OO languages
► We can do equational reasoning
► Haskell teaches the value of static types
► Compiler finds bugs long before run time
► We can express really detailed invariants
How do I “run”
Haskell?

20
GH
C

► We are going to use GHC in this course


► The (Glorious) Glasgow Haskell Compiler
► State-of-the-art and open source
► Windows and Mac
► Go to https://www.haskell.org/downloads
► Install Haskell Platform Full
► Linux
► sudo pkg-mgr install haskell-platform
► where pkg-mgr is your package manager: apt-get, yum,
emerge, …

21
Compiler versus interpreter
22

► Compiler (ghc)
► Takes one or more files as an input
► Generates a library or complete executable
► There is no interaction
► How you do things in
Imperatief/Mobiel/Gameprogrammeren
► Interpreter (ghci)
► Interactive, expressions are evaluated on-the-go
► Useful for testing and exploration
► You can also load a file
► Almost as if you have typed in the entire file
► repl.it is web-based ghci
GHC interpreter, ghci
1. Open a command line, terminal or console
► Right now, just repl.it
2. Write ghci and press
◻ GHCi, version 8.0.1: http://www.haskell.org/ghc/
Prelude>
3. Type an expression and press Enter
Prelude> 2 + 3
5
Prelude>
First examples
24

> length [1, 2, 3]


3
> sum [1 .. 10]
55
> reverse [1 .. 10]
[10,9,8,7,6,5,4,3,2,1]
> replicate 10 3
[3,3,3,3,3,3,3,3,3,3]
> sum (replicate 10 3)
30
Online Editor main = print(sum [1 .. 10])
► Integer numbers appear as themselves
► [1 .. 10] creates a list from 1 to 10
► Functions are called (applied) without parentheses
► In contrast to replicate(10, 3) in other languages
More about parentheses
25
► Parentheses delimit subexpressions
► sum (replicate 10 3): sum takes 1 parameter
► sum replicate 10 3: sum takes 3 parameters

> sum replicate 10 3


<interactive>: error:
• Couldn't match type ‘[t0]’ with ‘t1 ->
t’ Expected type: Int -> t0 -> t1 -> t
Actual type: Int -> t0 -> [t0]

> sum (replicate 10 3)


30
Operators
27

> [1, 2] ++ [3, 4]


[1, 2, 3, 4]
> (++) [1, 2] [3, 4]
> :t (++)
(++) :: [a] -> [a] -> [a]

► Some names are completely made out of symbols


► Think of +, *, &&, ||, …
► They are called operators
► Operators are used between the arguments
► Anywhere else, you use parentheses
Question

What happens if we do?

> [1, 2] ++ [True, False]

28
Question

What happens if we do?

> [1, 2] ++ [True, False]

Type error!

28
Define a function in a file
30

You can write this definition in a file

average :: [Int] -> Int


average ns = sum ns `div` length ns

and then load it in the interpreter


> :load average.hs
[1 of 1] Compiling Main ( average.hs, interpreted )
> average [1,2,3]
2
or even work on it an then reload
> :r
[1 of 1] Compiling Main ( average.hs, interpreted )
Define a function by cases
31

fac :: Int -> Int


fac 0 = 1
fac n = n * fac (n-1)
main = print(fac 5)

► Each equation goes into its own line


► Equations are checked in order
► If n is 0, then the function equals 1
► If n is different from 0, then it goes to the second
► Good style: always write the type of your functions
Question

What happens if we write?

fac :: Int -> Int


fac n = n * fac (n-1)
fac 0 = 1

32
More basic types
33

► Bool: True or False (note the uppercase!)


► Usual operations like && (and), || (or) and not
► Result of comparisons with ==, /=, <, …
► Warning! = defines, == compares

> 1 == 2 || 3 == 4
◻ False
> 1 < 2 && 3 < 4
◻ True
> nand x y = not (x && y)

> nand True False True


More basic types
34

► Char: one single symbol


► Written in single quotes: 'a', 'b', …
► String: a sequence of characters
► Written in double quotes: "hello"
► They are simply [Char]
► All list functions work for String
> ['a', 'b', 'c'] ++ ['d', 'e', 'f']
"abcdef"
>replicate 5 'a'
"aaaaa"
Homework
36

1. Install GHC in your machine


2. Try out the examples
3. Define some simple functions
► Sum from m to n
► Build greeter with two arguments
> greeter "morning" ["P", "Z"]
["Good morning, P!", "Good morning, Z!"]
4. Think about the types of those functions
A bit of history
38

Functional programming is quite old

► 1930s: Alonzo Church develops λ-calculus


► Theoretical basis for functional languages
► 1950s: McCarthy develops Lisp, the first FP language
► Lisp supports both statements and expressions
► Still in use: Common Lisp, Scheme, Racket
► 1970s: ML introduces type systems with inference
► 1990s: development of the Haskell language
► 1998: first stable version, Haskell’98
► 2010: current version of the Haskell language
Fun max (x,y) = if x >
y then x else y;

ML (META LANGUAGE)
A BRIEF INTRODUCTION
Meta language
◻ From a programming language perspective, a
metalanguage is a language used to make statements
regarding statements made in another language,
known as an object language. Metalanguage helps in
describing the concepts, grammar and objects
associated with a particular programming language.
◻ Metalanguage is widely used in language design,
analysers, compilers and theorem provers. It is also
used in financial systems, bioinformatics and in other
similar applications.
An Overview of ML
◻ ML means "Meta Language." It is a "purely"
functional/applicative language (this is actually a
white lie, but we'll focus primarily on the
functional part).
Application areas:
◻ Education
◻ Systems programming
◻ Important attributes/features that are similar to
those in Scheme/Lisp:Functional programming
style.
◻ Interpreter based.
◻ Heavy use of recursion and higher order functions.
◻ High level data structures well supported (eg.
lists)
◻ Garbage collection
New attributes/features:Statically/Strongly typed.
◻ Type inference

◻ Pattern matching.

◻ Polymorphism

◻ Compilation is well supported.

◻ Exception Handling
History
◻ Fig. 1 gives an abbreviated family DAG of the ML family, and a few related
languages. Dotted lines indicate significant omitted nodes. The rounded box
indicates those variants of the ML family that most people would call ML. A
brief history of ML:
◻ 1973: ML invented as part of the University of Edinburgh's LCF project, led by
Robin Milner et al., who were conducting research in constructing automated
theorem provers. Eventually observed that the "Meta Language" they used for
proving theorems was more generally useful as a programming language.
◻ Late 1970's: polymorphic type inference system completed by Milner and
Damas.
◻ Mid-80's: Standard ML; parameterized module system added by Dave
MacQueen; Caml fork at INRIA (France).
◻ 1996: Objective Caml, by Xavier Leroy et al.; adds inheritance to Caml module
system.
◻ 1997: ML97 revision of Standard ML.
Primitive ML data types

◻ numbers
integers (examples: 1, 4, ~3, 0) [Note the ~, not a -.]
reals (examples: 0.0, 3.5, 1.23E~10) [Note the ~.]
◻ booleans (true and false only)
◻ strings (eg. "foo" "bar\n")
◻ Case is significant (true, not TRUE).
Primitive ML operators

◻ Arithmetic (+, -, *, / (reals), div (integers), mod (integers), ~ (unary


minus))
◻ String concatenation ("house" ^ "cat")
◻ Relational (=, <, >, <=, >=, <>) are defined over numbers and strings
◻ Logical (andalso and orelse): short circuit logical operators.
◻ Conditional (if ... then ... else ...). This is an expression, not a control
flow statement. There is no if-then construct in ML. The then expression
is only evaluated if the conditional evaluates to true. The else expression
is only evaluated if the conditional evaluates to false. The if expression
and the then expression must be of the same type.
◻ Type coercion. ML is strongly and statically typed. There are no mixed
mode (3+3.0) expressions allowed. Use coercion operators: real
(int->real), floor (real->int), ceiling (real->int), truncate (real->int) [drop
digits to the right of decimal point], ord (char->int), chr (int->char).
type inference
◻ Notice that the syntax of declarations requires that the
programmer always explicitly specify the type. ML's
syntax doesn't require this, because ML has a type
inference system. Generally, ML will determine the
types of names and values based on how you use
them. You only need to declare the types of names
explicitly in certain cases when the type inference
algorithm doesn't have enough information to do it
automatically. To write down a value's type explicitly
is to ascribe the type to the value; in ML, the syntax
for ascription is expr:type or name:type, e.g.:
◻ - val someNumber = 16; (* this is what we type *)
◻ > val someNumber = 16 : int (* this is what the interpreter spits
back *)
◻ - val x = true;
> val x = true : bool
◻ - val y = (13 + 5) div 2;
> val y = 9 : int - y;
◻ > val it = 9 : int

◻ Notice in the above examples, that ML infers the type of the


variable for us. Also, when we evaluate an arbitrary expression,
the interpreter names the returned value "it."
Built-in compound data types

ML has several families of built-in data types; these


include:
◻ Records: fixed-size, heterogeneous collections
indexed by name
◻ Tuples: fixed-length, heterogeneous sequences
indexed by position
◻ Lists (singly linked): variable-length, homogeneous
sequences with O(1) time to prepend a value and O(n)
time to access a value in the list.
◻ Vectors: variable-length, homogeneous sequences
with O(1) access time for elements (i.e., like arrays)
Records
◻ Records resemble structs in C, or method-less objects
in Java; they are constructed by writing a list of one or
more field assignments name = value in between two
curly braces {}. Here are some examples:
◻ - val foo = {x = 3};
val foo = {x=3} : {x:int}
◻ - val bar = {x = 3, y = true};
val bar = {x=3,y=true} : {x:int, y:bool}
◻ - val baz = {x = "hi", y = foo};
val baz = {x="hi",y={x=3}} : {x:string, y:{x:int}}
◻ polymorphism.
◻ Fields of a record value are accessed using the
special function #fieldName applied
to recordValue:
◻ - val r = {x=1, y=2};
val r = {x=1,y=2} : {x:int, y:int}
❑ - #x(r);
val it = 1 : int
Tuples

◻ Tuples work a lot like records, except that the


fields have an explicit order; and instead of using
field names, you use positions to access the
members.
◻ Tuples are constructed simply by enclosing a
comma-separated list of two or more values in
round parentheses ():
◻ - val x = (54, "hello");
val x = (54,"hello") : int * string
◻ - val firstX = #1(x);
val firstX = 54 : int
◻ - val secondX = #2(x);
val secondX = "hello" : string
Lists

◻ Linked lists are the bread and butter of functional


programming. ML lists are homogeneous; that is,
all elements must have the same type.
◻ Lists may also be constructed from a
comma-separated list of values inside square
brackets []. This is syntactic sugar for a sequence
of conses; and, in fact, when you type a list of
conses at the repl, SML/NJ will answer using this
sugared syntax.
◻ - val x = 1::nil;
val x = [1] : int list
◻ - val y = 1::2::3::nil;
val y = [1,2,3] : int list
◻ - val z = 4::x;
val z = [4,1] : int list
Functions

◻ Syntax
◻ fun function-name (arg1, arg2, ...) = expression; (* general form *)
◻ - fun addtwo (x) = x+2.0; (* define a simple function *)
◻ > val addtwo = fn : real -> real

◻ Notice that ML deduced the type of the function. How? Why might
ML complain about the following function?

◻ - fun myadd (x, y) = x+y;


◻ ML uses sophisticated type inference mechanisms
that allow us to avoid explicitly declaring most
types. However, in the above declaration, it could
not determine the types of the arguments or result
type. Here's a fix:
◻ - fun myadd (x:real, y) = x+y;
◻ > val myadd = fn: real * real -> real
ML: An Example Program

◻ ML Computation: Calculation of Expressions


ML is a robust functional language that combines many of the
features of other programming languages we have studied in this
course.
The unit of evaluation in ML is the expression. Every expression
has a type, possibly a value, and may or may not cause an effect.
ML also limits defined functions to only take a single parameter,
however, this parameter may be a tuple, or a ‘unit’ (the empty
set).
ML is typically run in an interpretive manner.
■ (ML expression): fun average (x, y) = (x +y) / 2.0;
■ (User input): - average (5, 7);
■ (Resulting values): - val it = 6 : int
Type expressions

◻ The ML syntax for type expressions is probably


pretty strange at first sight. The type expressions
can consist of type names, combined by the
"operators" ->, *, and list. Let's look at some
examples:
◻ int list list of integers [int] int
* int list a tuple (int, [int]) (int *
int) list a list of tuples [(int, int)] int *
int list -> real a fun mapping tuples
(ints, [int]) to reals
◻ Here is the order of precidence from high to low:
◻ list
◻ *
◻ ->
All values are first-class citizens

◻ All ML's data values are first-class citizens,


meaning that all values have "equal rights": they
can all be passed to functions, returned from
functions, bound to names, stored as elements of
other values, etc.
Exercise: try writing code in Java, or your favorite other programming
language, that constructs objects that are roughly equivalent to the
above three values. How many lines does it take?
Why ML?

◻ ML is clean and powerful, and has many traits that


language designers consider hallmarks of a good high-level
language:
◻ Uniform reference model: all objects allocated in heap and
accessed by reference
◻ Garbage collected
◻ Strongly, statically typed, with an excellent type system
Strongly typed = programs cannot corrupt data by using it as
incorrect type.
Statically typed = types known and checked at compile time.
"Milner-Damas" type system hits a "sweet spot" combining
flexibility (polymorphism) with ease of use (type inference).
(Note: sometimes also called "Hindley-Milner" type system.)
◻ Highly orthogonal design: most features are independent
and (where sensible) arbitrarily combinable.
◻ Exceptions: well-defined error handling.
◻ Expression-oriented, not statement-oriented
◻ Sophisticated module system: powerful mechanisms for
organizing larger units of code. However, module system's
complexity is controversial, and rarely fully exploited in
practice.
◻ Clean syntax
◻ ML is the "exemplary" statically typed, strict functional
programming language.
An Overview of ML
◻ Description
ML is a high level functional programming language similar to LISP, Prolog,
and Hope.
ML is further differentiated by a strong and static type system, type inference,
and garbage collection.
ML was the first language to include statically checked polymorphic typing.

◻ History
ML was created as a metalanguage for the Edinburgh LCF proof assistant in
1973.
It has since developed into its own language, hence the acronymn ML no longer
is a relevant descriptor of the language.

Note - A data type that changes based on its current usage. For example, a programming
language that supports polymorphic typing allows the same routine to work on both integer
and floating point numbers, depending on which values are presented to it.
ML: An Example Program

◻ ML Computation: Calculation of Expressions


ML is a robust functional language that combines many of the
features of other programming languages we have studied in this
course.
The unit of evaluation in ML is the expression. Every expression
has a type, possibly a value, and may or may not cause an effect.
ML also limits defined functions to only take a single parameter,
however, this parameter may be a tuple, or a ‘unit’ (the empty
set).
ML is typically run in an interpretive manner.
■ (ML expression): fun average (x, y) = (x +y) / 2.0;
■ (User input): - average (5, 7);
■ (Resulting values): - val it = 6 : int
ML As a Functional Language: ML
is more Powerful!
◻ ML is Powerful:
ML has many of the attributes that you would expect in a
functional language like Scheme or LISP, such as:
■ Computation by expression rather than execution of
instruction
■ Declarative programming abstraction
■ Lambda calculus function methodology
Yet ML incorporates other features not usually found in
functional languages, such as:
■ Exception Handling
■ Garbage Collection
■ User defined Abstract Data Types
■ Strong and type handling and type inference
ML As a Functional Language:
Further Details
◻ ML exibits referential transparency
Variable assignment is a ‘once only’ operation.
Variable values do not change once assigned.
Referential transparency greatly increases the ability of a program to be
quantitatively analyzed, used as a proof, and debugged.
◻ ML variables are statically scoped
ML’s variables are entirely local to a called function
Variables are bound at run-time. Compiling is not often used.
Global variables don’t exist. Neither do instances of an object as might
be found in other languages supporting object abstractions.
◻ ML can handle many of the more interesting data types
Lists, Real Numbers, User defined ADTs
ML Functions: Extending the
Functional Languages
◻ ML functions may be of the highest order
ML uses aggressive type inference methods
ML has few standard data types, but all are extensible
Even Functions may define other Functions!, as in this
example:
Fun try (a, x) = a x;
> val try = fn : (‘a -> ‘b) * ‘a -> ‘b
Try (hd, [1,2,3];
>val it = 1 : int
Try (t1, [1,2,3]);
> val it = [2, 3] : int list
Such higher order functions have great utility and are part
of the attraction of a Functional language.
ML Functions: Extending the
Functional Languages
◻ ML supports Currying and Mapping:
Higher order functions may even embed arguments into the definition of a new
function.This is called ‘Currying’.
For Example:
■ Fun incr x y = x + y;
■ Val incr = fn : int -> int -> int
■ Incr 5 3;
■ Vali it = 8 : int
■ (incr 5) 3;
■ Val it = 8 : int
■ Val incr5 = incr 5;
■ Val incr5 = fn : int -> int
■ Incr5 3;
■ Val it = 8 : int
ML Polymorphism:

◻ ML broke programming ground with strong


polymorphism:
ML uses the term polymorphism to refer to the attribute of a
function to handle more than one type of parameter. List is
a good example. It works with any list of any type of
elements.
■ The list functions are similar to Scheme, only cleaner.
The ML conception of polymorphism is different than the
idea of method overloading that the term polymorphism can
also signify. We do not mean merely having two or more
functions with the same name.
ML functions are polymorphic if they contain only
polymorphic functions and operators. For example:
■ Fun revPair (x, y) = (y, x)
ML Functions: Local Variables are
‘Let’ into a Function
◻ ML allows local variables in a function.
The syntax for local variables follows the pattern :
■ Let………..in…………end
As we see in the following example:

Fun circleData (radius) =


Let
Val pi = 3.1415926536
Val circumference = two * pi * radius;
Fun area radius = pi * radius * radius
In
(circumference, area (radius))
End;
ML Flow Control: The Function
Stack
◻ Flow Control is an Interpretive Process
■ ML parses each line as an ML expression.
■ ML can use files and libraries, much like scheme.
■ ML performs I./O with simple grace, for example :
fun copyFile (inputFileName, outputFileName) =
Let
Val inFile = openIn inputFileName;
Val outFile = openOut outputFileName
In
( output (outFile, input inFile);
flushOut outFile;
closeOut outFile
)
End;
■ ML has been ported to many graphical windowing environments,
such as the unix/linux x-windowing system.
ML Handles Exceptions
◻ ML supports user-defined error flow control

Such exception handling makes ML an attractive


alternative to other Functional languages where this is
not possible
Conclusion: ML is a little known
giant
◻ ML should be better known and used:
ML is highly orthogonal. Most functions that work with one
type of parameter will work with any parameter. ML has
good internal type checking mechanisms which are applied
to every evaluated expression consistently.
Because ML allows for recursion, nested constructs, list
functions, and polymorphism, it is considered a highly
abstracted language, capable of implementing the most
complex of programming tasks with the most ease for the
programmer.
Conclusion: ML is a popular outcast

◻ ML in their words:
“ML has made significant inroads into the computer science
educational and research communities. The exposure of the
type mechanism at the source language level is a feature not
available in other widely used languages. However,
commercial applications of ML programs are few, and so
far it remains mostly a vehicle for computer science
research and educational use.” (That old book).
“…This may be because there are not enough…”
■ Gentle intro to ML: readers comments
Resources
◻ Textbook: Chapter 11
96

◻ Tutorial:
The Scheme Programming Language http://www.scheme.com/tspl3/
(Chapter 1-2)
Yet Another Haskell Tutorial http://www.cs.utah.edu/~hal/htut
(Chapter 1-4, 7)

◻ Implementation:
• DrScheme http://www.drscheme.org/

• Hugs http://www.haskell.org/hugs/ (download WinHugs)

◻ (Optional) Further reading:


Reference manual:
Haskell 98 Report http://haskell.org/haskellwiki/Definition
A Gentle Introduction to Haskell 98 http://www.haskell.org/tutorial/
Why does it matter, anyway?
97

The advantages of functional programming languages:


◻ Simple semantics, concise, flexible
◻ ``No’’ side effect
◻ Less bugs

It does have drawbacks:


◻ Execution efficiency
◻ More abstract and mathematical, thus more difficult to learn and use.

Even if we don’t use FP languages:


◻ Features of recursion and higher-order functions have gotten into most
programming languages.

Lecture 17 – Functional
CSE3302 Programming Languages, UT-Arlington ©Chengkai Li, 2008
Programming, Spring 2008
Scheme: Lisp dialect
◻ Syntax (slightly simplified):
98

expression → atom | list


atom → number | string | identifier | character | boolean
list → '(' expression-sequence ')'
expression-sequence → expression expression-sequence | expression

◻ Everything is an expression: programs, data, …


Thus programs are executed by evaluating expressions.
◻ Only 2 basic kinds of expressions:
atoms: unstructured
lists: the only structure (a slight simplification).
Expressions
99
42 —a number
"hello" —a string
#T —the Boolean value "true"
#\a —the character 'a'
(2.1 2.2 3.1) —a list of numbers
hello —a identifier
(+ 2 3) —a list (identifier "+" and two numbers)
(* (+ 2 3) (/ 6 2)) —a list (identifier "*" and two lists)
Evaluation of Expressions
100

Programs are executed by evaluating expressions. Thus


semantics are defined by evaluation rules of expressions.

Evaluation Rules:
◻ number | string: evaluate to itself

◻ Identifier: looked up in the environment, i.e., dynamically


maintained symbol table
◻ List: recursively evaluate the elements (more details in
following slides)
Eager Evaluation
101

◻ A list is evaluated by recursively evaluating each element:


• unspecified order
• first element must evaluate to a function.
This function is then applied to the evaluated values of the rest of the list.
(prefix form).
E.g.
3 + 4 * 5 (+ 3 (* 4 5))
(a == b)&&(a != 0) (and (= a b)(not(= a 0)))
gcd(10,35) (gcd 10 35)

◻ Most expressions use applicative order evaluation (eager evaluation): subexpressions are first
evaluated, then the expression is evaluated.
(correspondingly in imperative language: arguments are evaluated at a call site before they are
passed to the called function.)
Lazy Evaluation: Special Forms
102
◻ if function (if a b c):
a is always evaluated
Either b or c (but not both) is evaluated and returned as result.
c is optional. (if a is false and c is missing, the value of the expression is
undefined.)
e.g., (if (= a 0) 0 (/ 1 a))
◻ cond :
(cond (e1 v1) (e2 v2) ... (else vn))
The (ei vi) are considered in order
ei is evaluated. If it is true, vi is then evaluated, and the value is the result of
the cond expression.
If no ei is evaluated to true, vn is then evaluated, and the value is the result of
the cond expression.
If no ei is evaluated to true, and vn is missing, the value of the expression is
undefined.
(cond ((= a 0) 0) ((= a 1) 1) (else (/ 1 a)))
Lazy Evaluation: Special Forms
103
◻ define function:
declare identifiers for constants and function, and thus put them into symbol
table.

(define a b): define a name


(define (a p1 p2 …) b1 b2 …): define a function a with
parameters p1 p2 ….

the first expression after define is never evaluated.


e.g.,
define x (+ 2 3)

(define (gcd u v)
(if (= v 0) u (gcd v (remainder u v))))
Lazy Evaluation: Special Forms
104
◻ Quote, or ' for short, has as its whole purpose to not evaluate its
argument:
(quote (2 3 4)) or '(2 3 4) returns just (2 3 4).

(we need a list of numbers as a data structure)

◻ eval function: get evaluation back


(eval '(+ 2 3)) returns 5
Other Special Forms
105

◻ let function:
create a binding list (a list of name-value assocations), then
evaluate an expression (based on the values of the names)

(let ((n1 e1) (n2 e2) …) v1 v2 …)

e.g., (let ((a 2) (b 3)) (+ a b))

◻ Is this assignment?
Lists
106
List
Only data structure
Used to construct other data structures.
Thus we must have functions to manipulate lists.
◻ cons: construct a list
(1 2 3) = (cons 1 (cons 2 (cons 3 '())))
(1 2 3) = (cons 1 '(2 3))
◻ car: the first element (head), which is an expression
(car '(1 2 3)) = 1
◻ cdr:the tail, which is a list
(cdr '(1 2 3)) = (2 3)
Data structures
107

(define L '((1 2) 3 (4 (5 6))))


(car (car L))
(cdr (car L))
(car (car (cdr (cdr L))))

Note: car(car = caar


cdr(car = cdar
car(car(cdr(cdr = caaddr
Box diagrams
108
a List = (head expression, tail list)
• L = ((1 2) 3 (4 (5 6))) looks as follows in memory
Other list manipulation operations:
based on car, cdr, cons
109

◻ (define (append L M)
(if (null? L)
M
(cons (car L) (append (cdr L) M))
)
)

◻ (define (reverse L)
(if (null? L)
M
(append (reverse (cdr L)) (list (car L)))
)
)
Lambda expressions
110
/function values
◻ A function can be created dynamically using a lambda expression, which
returns a value that is a function:
(lambda (x) (* x x))

◻ The syntax of a lambda expression:


(lambda list-of-parameters exp1 exp2 …)

◻ Indeed, the "function" form of define is just syntactic sugar for a


lambda:
(define (f x) (* x x))
is equivalent to:
(define f (lambda (x) (* x x)))
Function values as data
111
◻ The result of a lambda can be manipulated as
ordinary data:

> ((lambda (x) (* x x)) 5)


25

> (define (add-x x) (lambda(y)(+ x y)))


> (define add-2 (add-x 2))
> (add-2 15)
17
Higher-order functions
112

• higher-order function:
a function that returns a function as its value
or takes a function as a parameter
or both
• E.g.:
• add-x
• compose (next slide)
Higher-order functions
113
(define (compose f g)
(lambda (x) (f (g x))))

(define (map f L)
(if (null? L) L
(cons (f (car L))(map f (cdr L)))))

(define (filter p L)
(cond
((null? L) L)
((p (car L)) (cons (car L)
(filter p (cdr L))))
(else (filter p (cdr L)))))
let expressions as lambdas:
114
◻ A let expression is really just a lambda applied immediately:
(let ((x 2) (y 3)) (+ x y))
is the same as
((lambda (x y) (+ x y)) 2 3)

◻ This is why the following let expression is an error if we want


x = 2 throughout:
(let ((x 2) (y (+ x 1))) (+ x y))
◻ Nested let (lexical scoping)
(let ((x 2)) (let ((y (+ x 1))) (+ x y)))

You might also like