Functional Programming
Functional programming Scala basics Control and data structures Higher order functions and closures Scala Library Note: Try typing examples from this slide in Eclipse to have the "feeling"
Functional Programming
Functional programming: Functions are values In C++, values are
Primitive types int, double, etc. Structure/class values Pointers
A function is not a first class value in C++
Cannot create new functions in a running program
In a functional programming language, functions are first-class values
Can have variables that hold functions Can create new functions
Introduction
The design of the imperative languages is based directly on the von Neumann architecture
Efficiency is the primary concern, rather than the suitability of the language for software development
The design of the functional languages is based on mathematical functions
A solid theoretical basis that is also closer to the user, but relatively unconcerned with the architecture of the machines on which programs will run
Mathematical Functions
A mathematical function is
a mapping of members of one set, called the domain set, to another set, called the range set
A lambda expression specifies the parameter(s) and the mapping of a function in the following form
(x) x * x * x
for the function
cube (x) = x * x * x
Lambda expressions describe nameless functions Lambda expressions are applied to parameter(s) by placing the parameter(s) after the expression
((x) x * x * x)(2)
which evaluates to 8
Higher-order Function
A higher-order function is one that either takes functions as parameters or yields a function as its result, or both Function Composition: A function that takes two functions as parameters and yields a function whose value is the first actual parameter function applied to the application of the second Example
h = f g
which means
h (x) = f ( g ( x))
For
f (x) = x + 2, h (x) = (3 * x)+ 2
g (x) = 3 * x
Apply-to-all: A functional form that takes a single function as a parameter and yields a list of values obtained by applying the given function to each element of a list of parameters E.g. For
h (x) = x * x ( h, (2, 3, 4)) yields (4, 9, 16)
Functional Programming Languages
The objective of the design of a FPL is to mimic mathematical functions to the greatest extent possible The basic process of computation is fundamentally different in a FPL than in an imperative language
In an imperative language, operations are done and the results are stored in variables for later use Management of variables is a constant concern and source of complexity for imperative programming
In an FPL, variables are not necessary, as is the case in mathematics Referential Transparency: the evaluation of a function always produces the same result given the same parameters
Why Scala?
Multi-paradigm language Invented by Martin Odersky at EPFL, Lausanne, Switzerland Similar to Java Works with Java tools/libraries/etc We use the functional subset instead of OCaml We use the combinator parser library instead of Antlr Fewer things to learn => Can get deeper where we want to go
Functional Programming in Scala
Functions can be values
val num = 3.14 val fun = math.ceil _ fun(num) // prints 4
Functions can be anonymous...
(x : Int) => x * x
...just like numbers
3.14
Of course, can put function values into variables and then use them
val square = (x : Int) => x * x square(10) // prints 100
...again, just like numbers
val pi = 3.14 pi * 10 // prints 31.4
Why Functional Programming?
Simpler and clearer programming style Often useful to pass functions as parameters
val numbers = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) numbers.map((x : Int) => x * x) // Prints Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
In C++, you can also do this, but there are no anonymous functions.
vector<int> map(vector<int> values, int (*f)(int)) { vector<int> result; for (int i = 0; i < size; i++) result.push_back(f(values[i])); return result; } int square(int x) { return x * x; } ... res = map(numbers, square); // Must pass name of function
In Scala, but not in C++, a function can produce a new function
Scala Basics
Types Int, Double, Boolean, String Arithmetic like in C++: + - * / % Variable type is inferred:
val luckyNumber = 13 // luckyNumber is an Int
Function types:
val square = (x : Int) => x * x // square is an Int => Int
Semicolons at the end of a line are optional
val x = 1 val y = 2 + // end line with operator to indicate that there is more to come 3
Everything is an object
1.to(10) // Apply to method to 1, returns Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Collections List (more in next lecture), Vector (similar to C++), Range
map works on any collection
1.to(10).map(x => x * x) // Type is inferred
Immutability
Immutable: Cannot change In Java, strings are immutable
"Hello".toUpper() doesn't change "Hello" but returns a new string "HELLO"
In Scala, val are immutable
val num = 3.14 num = 1.42 // Error
Pure functional programming: No mutations Don't mutatealways return the result as a new value Functions that don't mutate state are inherently parallelizable Important consideration in light of the end of Moore's Law
If/Else
if (booleanExpression) expression1 else expression2
if/else is an expression, not a statement. Can be used in other expressions:
val x = (if (true) 2 else 4) * 3
Like ? : in C++ Type is the most specific supertype common to expression1, expression2
val x = if (true) 3 else "Hello" // type is AnyVal
Omitting the else yields type Unit (like void in C++); not useful in functional programming
Recursion
def syntax for functions
def triple(x : Int) = 3 * x // same as val triple = (x : Int) => 3 * x
With recursive functions, also need to specify return type:
def fac(x : Int) : Int = if (x == 0) 1 else x * fac(x - 1)
Need def because the name is used on the right
val fac = if (x == 0) 1 else x * fac(x - 1) // fac not defined yet
Iteration (while, for) can always be expressed as recursion To iterate is human; to recurse, divine (L. Peter Deutsch)
Lists
Very different from C++ linked lists. No iterators Three primitives: head, tail, :: (pronounced cons) A list is either empty (Nil) or has a head and tail
val lst = List(1, 4, 9) lst.head // 1 lst.tail // List(4, 9) lst.tail.tail.tail // Nil
Use :: to build lists
0 :: lst // List(0, 1, 4, 9)
List Functions
Use recursion for list functions
def sum(lst : List[Int]) : Int = if (lst.isEmpty) 0 else // NOTE: else must be on this line lst.head + sum(lst.tail)
Use :: to recursively build lists
def squares(n : Int) : List[Int] = if (n == 0) List(0) else n * n :: squares(n - 1)
Why This Isn't Inefficient
lst.tail doesn't make a new listit is a reference to the tail cell
Works because lists are immutable Requires garbage collection
Functions as Parameters
Consider the map function
val triple = (x : Int) => 3 * x (1 to 10).map(triple) // yields 3 6 9 12 ... 30
Let's implement such a function. For simplicity, we only use sets and functions of Int Two parameters: List[Int], function (Int) => Int, returns List[Int]
def map(lst : List[Int], fun: (Int) => Int) : List[Int] =
Map of empty list is Nil, otherwise apply fun to lst.head and use recursion:
if (lst.isEmpty) Nil else fun(lst.head) :: map(lst.tail, fun)
Sample call:
map(List(1, 2, 3), (x : Int) => 3 * x)
A function describes a piece of behavior, such as What should map do with each element in lst?
Capturing the Enclosing Environment
Consider this function:
val n = 3 val fun = (x : Int) => n * x // What is fun(2)?
n is not defined in the scope of fun, but that is ok. In the body of a function, you can use any
variable from the enclosing scope. Doesn't seem a big dealn is immutable, so it will always be 3. But consider this:
def mulBy(n : Int) = (x : Int) => n * x
Huh? Let's call it:
val quadruple = mulBy(4) // the function (x : Int) => 4 * x
And let's call that:
quadruple(5) // yields 20
Each call to mulBy yields a different function. Each function has a different value of n Closure = function + binding of its free variables (i.e the variables that are not defined locally in the function)
Application: Event Handling
val textArea = new JTextArea(20, 50) val button1 = new JButton("Click Me!") button1.addActionListener((e : ActionEvent) => { textArea.append("I was clicked\n") })
Closure captures textArea Full code here (if you are interested) Tedious in Java; requires inner class
final JTextArea textArea = new JTextArea(20, 50); button1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { textArea.append("Hello\n"); } };
Type Inference
Scala is strongly typed. Any value has a type guarantee. Just like C++:
char* greeting = "Hello"; greeting = 42; // Error
But without having to declare types:
var greeting = "Hello" // String is inferred greeting = 42 // Error
Contrast with scripting languages such as JavaScript
var greeting = 'Hello' // This is JavaScript greeting = 42 // Ok alert(greeting.length) // Runtime error: 42 doesn't have a length member
Type Inference 2
Can override inferred type (only to a supertype, of course)
var greeting : Any = "Hello" greeting = 42 // Ok
Parameter types must be specified (because they are not initialized when they are declared)
def mystery(x) = 42 * x // Error def mystery(x : Int) = 42 * x // Ok
Function return type is inferred
If x is Int, then 42 * x is also Int
Exception: Recursive function
def fac(x : Int) = if (x == 0) 1 else x * fac(x - 1)
Can't get type of right hand side without knowing type of fac Limitation of Scala: Other languages (e.g. Haskell) can infer recursive types through unification
Parameter Inference From Context
When a function parameter type is known, you can supply an anonymous function without specifying its parameter types
def twice(f: (Int)=>Int, x : Int) = f(f(x)) twice(x => 42 * x, 3) // Ok, x : Int is inferred from context
Very useful when calling library functions
List(1, 2, 3).filter((x) => x % 2 == 0)
List[A].filter (p : (A) => Boolean) : List[A] A is Int since List(1, 2, 3) is a List[Int] f must be (Int)=>Boolean x must be Int
Parameter Simplifications
Ok to omit () around a single inferred parameter
List(1, 2, 3).filter(x => x % 2 == 0) List(1, 2, 3).sortWith((x, y) => x > y) // need () with 2 or more parameters
Use _ for a parameter that only occurs once in the body
List(1, 2, 3).filter(_ % 2 == 0) List(1, 2, 3).sortWith(_ > _)
Control Abstractions
Methods such as map and filter have as argument a function (i.e. code) A while loop has two arguments: a condition (code) and a body (code) Could we implement while in Scala? Sure:
def While(cond: () => Boolean, body: () => Unit) { if (cond()) { body(); While(cond, body) } }
But calling is a bit ugly:
var x = 1 While (() => x < 10, () => { println(x); x+= 1 })
By-Name Parameters
Control abstraction = User or library function that looks to programmers like a language mechanism Want nice syntax, e.g. {} for blocks without () => Use by-name parameters
def While(cond: => Boolean, body: => Unit) { // Not () => if (cond) { // NOTE: No () in call body; While(cond, body) } }
Now the unsightly () => are gone from the call:
While ({ x < 10 }, { x += 1; println(x) })
Still unhappy about unsightly ( , ) in call.
Currying
Currying = Turning a function that takes two arguments into a function that takes one argument. That function returns a function that consumes the second argument. (Named after the logician Haskell Brooks Curry)
Huh? Simple example:
def mul(x : Int, y : Int) = x * y mul(3, 4) is 12 def mul2(x : Int)(y : Int) = x * y mul2(3)(4) is 12
What is mul2(3)? A function that is capable of eating 4, thus yielding 12
y : Int => 3 * y
The While Control Abstraction
def While(cond: => Boolean)(body: => Unit) { if (cond) { body; While(cond)(body) } } While (x < 20) { x += 1; println(x) }
Trick #1: By-name parameters Trick #2: Currying * Note: While {x < 20} also works
Scala Documentation
Online at http://scala-lang.org/ Be smart: Download and install it locally Be smart: Bookmark it Use text field in upper left corner to filter classes
Categories of List Methods
Basic methods length head tail isEmpty Operators :: :+ ++ == != /: :\ Access by position (n) take drop dropRight slice indexOf lastIndexOf Methods with unary predicates count exists dropWhile filter find
findIndexOf forall partition remove span takeWhile Methods with unary function map reverseMap flatMap foreach Methods with binary predicate sort Methods with binary function reduceLeft reduceRight foldLeft foldRight Other intersection union zip zipAll zipWithIndex mkString
List Operators
:: appends in front, :+ in back
3 :: List(1, 2) is List(3, 1, 2) List(1, 2) :+ 3 is List(1, 2, 3)
++ concatenate lists
List(1, 2) ++ List(3, 4) is List(1, 2, 3, 4) // same as :::
== and != compare lists
List("Hello", "World") == List("Hel" + "lo", "Wor" + "ld")
/: and :\ later
Operator Precedence and Associativity
On this calculator, what do you get when you type 1 + 2 * 3? What do you get in C++? Precedence: Which operator is executed first?
(1 + 2) * 3 1 + (2 * 3)
Associativity: If the same operator occurs twice, is the left or right one executed first? Does 1 2 - 3 mean
(1 - 2) - 3 1 - (2 - 3)
In C++, most operators are left-associative. Exception: Assignment
a = b = 3
Scala Operators
Function names can be any sequence of opchars: characters other than whitespace, A-Z0-
9()[]{}`'".,;
Operator precedence depends on first character
(all letters) | ^ & < > = ! : + * / % (all other special characters)
Operators ending in : are right associative; all others are left associative
a :: b :: Nil is a :: (b :: Nil)
Lists: Access by Position
Not very efficient for linked lists Index values are 0-based () are used for indexed accessnot []
List(17, 29)(1) is 29
slice takes sublist
List(2, 3, 5, 7).slice(1, 3) is List(3, 5)
Arguments to slice are
first index to include first index to exclude
See lab for drop take
Methods with Function Parameters
Workhorse functions of the library You already saw filter, sort, map Others explored in lab Instead of looping, build a function and pass it to one of these methods
def randList(len : Int, n : Int) = (1 to len).map((x : Int) => gen.nextInt(n))
Function can be shortenedsee next lecture Predicate = function returning Boolean
Some methods return a pair
List(2,3,5,7).partition(isEven) is (List(2),List(3, 5, 7))
Tuples
A pair is an example of a tuple If S, T, U are any types, then we have tuple types (S, T), (S, T, U) Ex. ("Hello", 1729) is an instance of (String, Int) Use methods _1 _2 etc. to access members (not zero-based!)
("Hello", 1729)._2 is 1729
Convenient if a method returns more than one piece of information Don't use tuples if the data has semantics. Ex. Don't use (day, month, year). Make (or use) a Date class.
Folding
The sum of the elements in List(a, b, c) is
a + b + c = 0 + a + b + c = ((0 + a) + b) + c
Use foldLeft or the /: operator
def sum(lst: List[Int]) = (0 /: lst) ((x, y) => x + y)
The /: indicates the tree shape
+ / \ + / \ + b / \ 0 a c
The first argument of the folding function is the partial answer; the second is the new list value to be considered To recurse may be divine, but not to recurse is even better:
def fac(n : Int) = (1 /: (1 to n)) {_ * _}
The foldRight operator works right-to-left: a + (b + (c + 0))
mkString
toString produces the familiar List(1, 2, 3) What if we want 1 | 2 | 3?
lst.mkString("|")
One fewer separator than elements
What if we want [1 | 2 | 3]?
lst.mkString("[", "|", "]")
Nice attention to detail by the library designers
Comparing Functional and Imperative Languages
Imperative Languages:
Efficient execution Complex semantics Complex syntax Concurrency is programmer designed
Functional Languages
Simple semantics Simple syntax Inefficient execution Programs can automatically be made concurrent
Summary
Functional programming languages use function application, conditional expressions, recursion, and functional forms to control program execution instead of imperative features such as variables and assignments Purely functional languages have advantages over imperative alternatives, but their lower efficiency on existing machine architectures has prevented them from enjoying widespread use