This document discusses functional objects in Scala. It covers several key points in 3 sentences:
Functional objects are immutable objects without mutable state. They define methods that do not change the object but return a new value. This allows natural and concise notation for methods used as infix operators and method overloading without side effects. The document also discusses defining operators, adding fields, self references, auxiliary constructors, and private fields and methods to implement functional objects in Scala.
5. Binding Variable Identifiers
name binding
binding
defining occurrence
val msg = "Hello, " + "world!"
println(msg)
applied occurrence
val variables cannot be rebound
B:6.1 TI 1220 - Lecture 2: Functional Objects 5
6. Binding Variable Identifiers
name binding
binding
defining occurrence
var greeting = "Hello, world!"
greeting = "Leave me alone, world!"
rebinding
var variables can be rebound
B:6.1 TI 1220 - Lecture 2: Functional Objects 6
7. Binding Function Identifiers
name binding
binding
defining occurrence
def widthOfLength(s: String) =
s.length.toString.length
val maxWidth = widthOfLength(longestLine)
applied occurrence
B:6.1 TI 1220 - Lecture 2: Functional Objects 7
8. Binding Class Identifiers
name binding
binding
defining occurrence
class ChecksumAccumulator {
var sum = 0
}
var acc = new ChecksumAccumulator
var csa = new ChecksumAccumulator
acc.sum = 3
csa = acc applied occurrence
TI 1220 - Lecture 2: Functional Objects 8
9. Rebinding vs Mutation
name binding
class ChecksumAccumulator {
var sum = 0
} binding
var acc = new ChecksumAccumulator
var csa = new ChecksumAccumulator
mutation
acc.sum = 3
csa = acc
rebinding
TI 1220 - Lecture 2: Functional Objects 9
10. Name Spaces
name binding
object foo {
val foo : Int = 0
def foo(x : Int) = x + 1
}
object bar {
def bar() = foo.foo(foo.foo)
}
variables, functions, objects are in separate name spaces
B:6.1 TI 1220 - Lecture 2: Functional Objects 10
11. Substitution
name binding
val msg = "Hello, " + "world!"
println(msg)
?
println("Hello, " + "world!")
can we replace applied occurrence with bound expression/value?
B:6.1 TI 1220 - Lecture 2: Functional Objects 11
14. Constructing a Rational
class parameters
class Rational(n: Int, d: Int) {
println("Created " + n + "/" + d)
}
scala> new Rational(1, 2)
Created 1/2
res0: Rational = Rational@2d83e895
B:6.2 TI 1220 - Lecture 2: Functional Objects 14
15. Immutable Object Trade-offs
Advantages
• easier reasoning
• pass around freely (no risk of undesired mutations)
• cannot be changed concurrently in two threads
• immutable object make safe hashtable keys
Disadvantages
• copying large object graphs vs in-place update
B:6.1 TI 1220 - Lecture 2: Functional Objects 15
16. Reimplementing toString
overriding methods
class Rational(n: Int, d: Int) {
override def toString = n + "/" + d
}
scala> val half = new Rational(1, 2)
half: Rational = 1/2
B:6.3 TI 1220 - Lecture 2: Functional Objects 16
17. Checking Preconditions
class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n + "/" + d
}
scala> val half = new Rational(1, 0)
java.lang.IllegalArgumentException: requirement failed
B:6.4 TI 1220 - Lecture 2: Functional Objects 17
18. Visibility of Class Parameters
class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n + "/" + d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
}
$ fsc Rational.scala
Rational.scala:5: error: value d is not a member of Rational
new Rational(n * that.d + that.n * d, d * that.d)
^
Rational.scala:5: error: value d is not a member of Rational
new Rational(n * that.d + that.n * d, d * that.d)
^
two errors found
B:6.5 TI 1220 - Lecture 2: Functional Objects 18
19. Adding Fields
class Rational(n: Int, d: Int) {
require(d != 0)
val numer: Int = n
val denom: Int = d
override def toString = numer + "/" + denom
def add(that: Rational): Rational =
new Rational(
numer * that.denom + that.numer * denom,
denom * that.denom)
}
scala> new Rational(1,2) add new Rational(2,3)
res0: Rational = 7/6
B:6.5 TI 1220 - Lecture 2: Functional Objects 19
20. Non-Functional Objects
destructive update
class ImperativeRational(n: Int, d: Int) {
require(d != 0)
var numer: Int = n
var denom: Int = d
override def toString = numer + "/" + denom
def add(that: ImperativeRational) {
numer = numer * that.denom + that.numer * denom;
denom = denom * that.denom;
}
} scala> val half = new ImperativeRational(1, 2)
half: ImperativeRational = 1/2
scala> val twothirds = new ImperativeRational(2,3)
twothirds: ImperativeRational = 2/3
scala> half.add(twothirds)
scala> half
res1: ImperativeRational = 7/6
B:6.1 TI 1220 - Lecture 2: Functional Objects 20
21. Self References
this
def lessThan(that: Rational) =
this.numer * that.denom < that.numer * this.denom
def max(that: Rational) =
if (this.lessThan(that)) that else this
B:6.6 TI 1220 - Lecture 2: Functional Objects 21
22. Auxiliary Constructors
class Rational(n: Int, d: Int) {
require(d != 0)
val numer = n
val denom = d
def this(n: Int) = this(n, 1) // auxiliary constructor
...
}
scala> new Rational(6)
res1: Rational = 6/1
B:6.7 TI 1220 - Lecture 2: Functional Objects 22
23. Private Fields and Methods
class Rational(n: Int, d: Int) {
require(d != 0)
private val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g
...
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
scala> new Rational(6,42)
res1: Rational = 1/7
B:6.7 TI 1220 - Lecture 2: Functional Objects 23
24. Defining Operators
use functions as infix operators
def add(that: Rational): Rational =
new Rational(numer * that.denom + that.numer * denom,
denom * that.denom)
scala> new Rational(1,2).add(new Rational(2,3))
res0: Rational = 7/6
scala> new Rational(1,2) add new Rational(2,3)
res0: Rational = 7/6
B:6.7 TI 1220 - Lecture 2: Functional Objects 24
26. Defining Operators
operator identifiers
def +(that: Rational): Rational =
new Rational(numer * that.denom + that.numer * denom,
denom * that.denom)
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
scala> val d = a + b * c
d: Rational = 11/14
scala> val d = a.+(b.*(c))
d: Rational = 11/14
scala> val d = a * b + c
d: Rational = 16/21
scala> val d = (a.*(b)).+(c)
d: Rational = 16/21
B:6.7 TI 1220 - Lecture 2: Functional Objects 26
27. Identifiers
lexical syntax
Alphanumeric identifier
• identifier: [$A-Za-z_][$A-Za-z_0-9]* ($ reserved for Scala compiler)
• camel-case convention: toString, HashSet
Operator identifier
• Unicode set of mathematical symbols(Sm) or other symbols(So), or to
the 7-bit ASCII characters that are not letters, digits, parentheses,
square brackets, curly braces, single or double quote, or an underscore,
period,semi-colon, comma, or back tick character.
Literal Identifier
• arbitrary string enclosed in back ticks (` . . . `).
B:6.1 TI 1220 - Lecture 2: Functional Objects 27
28. Method Overloading
for different argument types
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
scala> val c = new Rational(3,7)
c: Rational = 3/7
scala> c * 2
res1: Rational = 6/7
B:6.1 TI 1220 - Lecture 2: Functional Objects 28
29. Method Overloading
does not apply to this
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
scala> 2 * c
<console>:7: error: overloaded method value * with alternatives: (Double)
Double <and> (Float)Float <and> (Long)Long <and> (Int)Int <and> (Char)Int
<and> (Short)Int <and> (Byte)Int cannot be applied to (Rational)
2 * c
^
B:6.1 TI 1220 - Lecture 2: Functional Objects 29
30. Implicit Conversions
def *(that: Rational): Rational =
new Rational(numer * that.numer, denom * that.denom)
def *(i: Int): Rational =
new Rational(numer * i, denom)
implicit def intToRational(x: Int) = new Rational(x)
scala> 2 * c
res4: Rational = 6/7
B:6.1 TI 1220 - Lecture 2: Functional Objects 30
31. Summary
functional objects
Immutable objects
• class parameters
• immutable fields (val)
• methods don’t change object, but return value
Natural, concise notation
• methods as infix operators, operator identifiers
• method overloading
• implicit conversion
B:6.1 TI 1220 - Lecture 2: Functional Objects 31
32. Exam Question
How many Rational objects are created while executing:
class Rational(n: Int, d: Int) {
require(d != 0)
val numer: Int = n
val denom: Int = d
override def toString = numer + "/" + denom
def +(that: Rational): Rational =
new Rational(
numer * that.denom + that.numer * denom,
denom * that.denom)
}
var half = new Rational(1,2)
half = half + half + half
(a) 1
(b) 2
(c) 3
(d) 4
TI1220 - Introduction 32
35. Control-Flow Structures
Control-Flow Structures
• ordering execution
• choice, iteration, exception
Functional Control Structures
• can be used as expression
• return a value
Minimal set of built-in control structures
• control abstraction using function literals (next week)
B:6.1 TI 1220 - Lecture 2: Functional Objects 35
36. If Expressions
conditional choice
var filename = "default.txt"
if (!args.isEmpty) imperative style
filename = args(0)
val filename =
if (!args.isEmpty) args(0) functional style
else "default.txt"
B:7.1 TI 1220 - Lecture 2: Functional Objects 36
37. Equational Reasoning
replace equals by equals
val filename =
if (!args.isEmpty) args(0)
else "default.txt"
println(filename)
println(if (!args.isEmpty) args(0) else "default.txt")
B:7.1 TI 1220 - Lecture 2: Functional Objects 37
38. Equational Reasoning
replace equals by equals
val x = e; f(x) <=> f(e)
if e has no side effects
B:7.1 TI 1220 - Lecture 2: Functional Objects 38
39. While Loops
iteration
def gcdLoop(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val temp = a
a = b % a
b = temp
}
b
}
B:7.2 TI 1220 - Lecture 2: Functional Objects 39
40. Do-While Loops
iteration
var line = ""
do {
line = readLine()
println("Read: " + line)
} while (line != "")
B:7.2 TI 1220 - Lecture 2: Functional Objects 40
41. Assignment has Type Unit
var line = ""
while ((line = readLine()) != "") // This doesn’t work!
println("Read: " + line)
B:7.2 TI 1220 - Lecture 2: Functional Objects 41
42. Iteration vs Recursion
def gcd(x: Long, y: Long): Long =
if (y == 0) x else gcd(y, x % y)
def gcdLoop(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val temp = a
a = b % a
b = temp
}
b
}
B:7.2 TI 1220 - Lecture 2: Functional Objects 42
43. For Expressions
functional iteration
val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere)
println(file)
B:7.3 TI 1220 - Lecture 2: Functional Objects 43
44. Ranges
iterating over sequence of numbers
scala> for (i <- 1 to 4)
| println("Iteration " + i)
Iteration 1
Iteration 2
Iteration 3
Iteration 4
// Not common in Scala...
for (i <- 0 to filesHere.length - 1)
println(filesHere(i))
B:7.3 TI 1220 - Lecture 2: Functional Objects 44
45. Filtering
iterating over subset of a collection
for (file <- filesHere)
if (file.getName.endsWith(".scala"))
println(file)
for (file <- filesHere if file.getName.endsWith(".scala"))
println(file)
B:6.1 TI 1220 - Lecture 2: Functional Objects 45
46. Multiple Filters
for (
file <- filesHere if file.isFile;
if file.getName.endsWith(".scala")
) println(file)
B:7.3 TI 1220 - Lecture 2: Functional Objects 46
47. Nested Iteration
def fileLines(file: java.io.File) =
scala.io.Source.fromFile(file).getLines.toList
def grep(pattern: String) =
for (
file <- filesHere if file.getName.endsWith(".scala");
line <- fileLines(file) if line.trim.matches(pattern)
) println(file + ": " + line.trim)
grep(".*gcd.*")
B:7.3 TI 1220 - Lecture 2: Functional Objects 47
48. Mid-Stream Variable Binding
def grep(pattern: String) =
for {
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(pattern)
} println(file + ": " + trimmed)
grep(".*gcd.*")
B:7.3 TI 1220 - Lecture 2: Functional Objects 48
49. Producing a New Collection
yield
def scalaFiles =
for {
file <- filesHere
if file.getName.endsWith(".scala")
} yield file
B:7.3 TI 1220 - Lecture 2: Functional Objects 49
50. Composing filters
val forLineLengths =
for {
file <- filesHere
if file.getName.endsWith(".scala")
line <- fileLines(file)
trimmed = line.trim
if trimmed.matches(".*for.*")
} yield trimmed.length
B:7.3 TI 1220 - Lecture 2: Functional Objects 50
51. Throwing Exceptions
exception handling
val half =
if (n % 2 == 0)
n / 2
else
throw new RuntimeException("n must be even")
B:7.4 TI 1220 - Lecture 2: Functional Objects 51
52. Catching Exceptions
exception handling
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("input.txt")
// Use and close file
} catch {
case ex: FileNotFoundException => // Handle missing file
case ex: IOException => // Handle other I/O error
}
B:7.4 TI 1220 - Lecture 2: Functional Objects 52
54. Yielding a Value
exception handling
import java.net.URL
import java.net.MalformedURLException
def urlFor(path: String) =
try {
new URL(path)
} catch {
case e: MalformedURLException =>
new URL("http://www.scalalang.org")
}
B:7.4 TI 1220 - Lecture 2: Functional Objects 54
55. Match Expressions
choosing between actions
val firstArg = if (args.length > 0) args(0) else ""
firstArg match {
case "salt" => println("pepper")
case "chips" => println("salsa")
case "eggs" => println("bacon")
case _ => println("huh?")
}
B:7.5 TI 1220 - Lecture 2: Functional Objects 55
56. Match Expressions
choosing between values
val firstArg = if (!args.isEmpty) args(0) else ""
val friend =
firstArg match {
case "salt" => "pepper"
case "chips" => "salsa"
case "eggs" => "bacon"
case _ => "huh?"
}
println(friend)
B:7.5 TI 1220 - Lecture 2: Functional Objects 56
57. Break and Continue in Java
not in Scala
int i = 0; // This is Java
boolean foundIt = false;
while (i < args.length) {
if (args[i].startsWith("")) {
i = i + 1;
continue;
}
if (args[i].endsWith(".scala")) {
foundIt = true;
break;
}
i = i + 1;
}
B:7.6 TI 1220 - Lecture 2: Functional Objects 57
58. Replace Break/Continue with If ...
var i = 0
var foundIt = false
while (i < args.length && !foundIt) {
if (!args(i).startsWith("")) {
if (args(i).endsWith(".scala"))
foundIt = true
}
i = i + 1
}
B:7.6 TI 1220 - Lecture 2: Functional Objects 58
59. ... or with Recursion
def searchFrom(i: Int): Int =
if (i >= args.length) 1
else if (args(i).startsWith("")) searchFrom(i + 1)
else if (args(i).endsWith(".scala")) i
else searchFrom(i + 1)
val i = searchFrom(0)
B:7.6 TI 1220 - Lecture 2: Functional Objects 59
60. Variable Scope
almost identical to Java
Variables are declared in a scope
• { var j = 1; ... }
Inner scopes shadow variables in outer scopes
• { var j = 1; { var j = 2; ... } }
B:7.7 TI 1220 - Lecture 2: Functional Objects 60
62. def printMultiTable() {
var i = 1
// only i in scope here
while (i <= 10) {
var j = 1
// both i and j in scope here
while (j <= 10) {
val prod = (i * j).toString
// i, j, and prod in scope here
var k = prod.length
// i, j, prod, and k in scope here
while (k < 4) {
print(" ")
k += 1
}
print(prod)
j += 1
}
// i and j still in scope; prod and k out of scope
println()
i += 1
}
// i still in scope; j, prod, and k out of scope
}
B:7.7 TI 1220 - Lecture 2: Functional Objects 62
63. Refactoring Imperative-Style Code
// Returns a row as a sequence
def makeRowSeq(row: Int) =
for (col <- 1 to 10) yield {
val prod = (row * col).toString
val padding = " " * (4 - prod.length)
padding + prod
}
// Returns a row as a string
def makeRow(row: Int) = makeRowSeq(row).mkString
// Returns table as a string with one row per line
def multiTable() = {
val tableSeq = // a sequence of row strings
for (row <- 1 to 10)
yield makeRow(row)
tableSeq.mkString("n")
}
B:6.1 TI 1220 - Lecture 2: Functional Objects 63
65. Summary
lessons learned
Functional Objects
• immutable objects
• operations create new objects
Functional Control Structures
• return a value
• have a type
• can be used as expressions
TI 1220 - Lecture 2: Functional Objects 65
67. Exercises Week 2
complex numbers
Scala Test
• using unit testing framework to define executable tests
Complex numbers
• define class to represent complex numbers
• define tests
Note: all assignments should be done individually!
B:6.1 TI 1220 - Lecture 2: Functional Objects 67
70. Outlook
coming next
Lecture 3: Functions & Closures
• Chapters 8, 9
Lecture 4: List Programming
• Chapters 15, 16, 17
Lecture 5: Trees
• Chapters 26, 22, 23
Lab Week 2
• Complex numbers in Scala
B:6.1 TI 1220 - Lecture 2: Functional Objects 70
71. Exam Question (Week 1)
What happens when we execute the following code:
val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!n"
(a) Error: greetStrings is an immutable variable that cannot
be assigned to
(b) Error: Array is a immutable data structure that cannot
be assigned to
(c) No error: greetStrings is a mutable variable that can be
assigned to
(d) No error: Array is a mutable data structure that can be
assigned to
TI1220 - Introduction 71
72. Exam Question (Week 1)
What happens when we execute the following code:
val greetStrings = new Array[String](3)
greetStrings(0) = "Hello"
greetStrings(1) = ", "
greetStrings(2) = "world!n"
(a) Error: greetStrings is an immutable variable that cannot
be assigned to
(b) Error: Array is a immutable data structure that cannot
be assigned to
(c) No error: greetStrings is a mutable variable that can be
assigned to
(d) No error: Array is a mutable data structure that can be
assigned to
TI1220 - Introduction 72
73. Exam Question (Week 2)
What is the return type of makeRowSeq:
def makeRowSeq(row: Int) =
for (col <- 1 to 10) yield {
val prod = (row * col).toString
val padding = " " * (4 - prod.length)
padding + prod
}
(a) String
(b) Int
(c) IndexedSeq[String]
(d) IndexedSeq[Int]
TI1220 - Introduction 73
74. Pictures
copyrights
Slide 1:
A Plumpish Proportion by SSG Robert Stewart some rights reserved
Slide 3:
McPhillips’ Map of the City of Winnipeg by Manitoba Historical Maps, some rights reserved
Slide 19:
Envelopes by benchilada, some rights reserved
Slide 20:
Report card by Carosaurus, some rights reserved
Slide 30:
Sun is Shining by el patojo, some rights reserved
B:6.1 TI 1220 - Lecture 2: Functional Objects 74
75. Pictures
copyrights
Slide:
Dinner at Roussillon (Martin Odersky) by Miles Sabin, some rights reserved
Slide:
Portrait: Alex Payne by Dave Fayram, some rights reserved
Slide:
“Plumbing Nightmare” by Natalie Wilkie, some rights reserved
Slide:
HIV: The Moleskine Summary by Niels Olson
Slide:
remember to thank all the books you haven’t read over the past three years
by Natalia Osiatynska, Some rights reserved
Slide:
Stupid Exam by Remi Carreiro, Some rights reserved
Slide:
Practice makes perfect by Simon Probert
http://www.flickr.com/photos/garrettc/3747802654/ Bombe detail by Garret Coakley, Some rights
reserved
B:6.1 TI 1220 - Lecture 2: Functional Objects 75