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

Unifying Oo and Functional Programming Jorge Ortiz

This document discusses how Scala unifies object-oriented and functional programming. It begins by defining object-oriented programming and functional programming. It then explains how Scala incorporates functional programming concepts like pattern matching, functions as first-class values, and lazy evaluation into an object-oriented language through features like case classes, extractors, and lazy vals. The document argues this allows Scala to effectively fuse OOP and FP.

Uploaded by

Karthik Iyer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
46 views

Unifying Oo and Functional Programming Jorge Ortiz

This document discusses how Scala unifies object-oriented and functional programming. It begins by defining object-oriented programming and functional programming. It then explains how Scala incorporates functional programming concepts like pattern matching, functions as first-class values, and lazy evaluation into an object-oriented language through features like case classes, extractors, and lazy vals. The document argues this allows Scala to effectively fuse OOP and FP.

Uploaded by

Karthik Iyer
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

Unifying Object-Oriented

and Functional Programming

Jorge Ortiz
CS94SI
“Scala goes further than all other well-
known languages in fusing object-
oriented and functional programming.”

- Martin Odersky, creator of Scala


What is OOP?

• Main goals:
• Abstraction
• Code reuse
• Unifying data and behavior
• Controlling complexity
What is OOP?

• It attempts to achieve these goals with:


• Modularity
• Inheritance
• Encapsulation
• Polymorphism
What is FP?

• Most restrictive definition: Pure functional


languages = no side effects
• More general definition: “Impure” languages
have some functional aspects in common
What is FP?
• FP can include:
• garbage collection
• parametrized types
• pattern matching
• functions as first-class values
• lazy evaluation
"We were after the C++ programmers.
We managed to drag a lot of them about
halfway to Lisp."

- Guy Steele, co-author of the Java spec


FP + OOP
• Some FP concepts have found good homes
in OO languages
• Garbage collection (Smalltalk, Java, C#)
• Parametrized types (C++, Java, C#)
• Functions as first-class values (Smalltalk,
C#, Java?)
Pattern Matching
Pattern Matching?

• Few languages support both Object-


Oriented Programming and Pattern
Matching
• Why?
• OOP and PM deal with very different
problem domains
PM + OOP?

• OOP is good for problems where:


• Fixed set of methods to be supported
• Extensible set of objects supporting them
• Good example: GUI library
PM + OOP?

• Pattern matching is good for problems:


• Fixed set of data types to be supported
• Extensible set of methods on those types
• Good example: Compiler
PM + OOP?

• Why not include PM in OO languages?


• Three OO criticism of pattern matching:
• Unnecessary
• Not extensible
• Breaks encapsulation
PM + OOP?
• Unnecessary – use visitor design pattern
interface Visitor { class Sum {
void visit(Sum sum); public void accept(Visitor visitor) {
void visit(Product product); visitor.visit(this);
void visit(Constant constant); }
... ...
} }

• Challenge: Do HW#3 with visitors


• Verbose
• Doesn’t allow nested patterns
PM + OOP?
• Not extensible, algebraic data types
have a fixed number of cases
• This is true in some functional languages
• Not true in Scala
• case classes can both inherit and be
inherited from
PM + OOP?
• Breaks encapsulation
• In most functional languages, cases are a
thin wrapper around data
• OOP is about hiding data, pattern
matching is about exposing it
• In Scala, case classes can have private
members too
• Consider constructor parameters part
of the contract for the class
PM + OOP in Scala

• Case classes
• Scala’s version of algebraic data types
• Extractors
• A way for pattern matching to cohabit
with OO data abstraction
Extractors

• Extractors are used for pattern matching


on non-case classes
• Useful to think of them as user-defined
views on or into objects
Extractors
val s: String = “jorge@gmail.com”
s match {
case EMail(name, domain) =>
println(name + “ AT ” + domain)
case _ =>
println(“Not an e-mail address”)
}

// prints “jorge AT gmail.com”


Extractors
val s: String = “Some Junk String”
s match {
case EMail(name, domain) =>
println(name + “ AT ” + domain)
case _ =>
println(“Not an e-mail address”)
}

// prints “Not an e-mail address”


Extractors: How?
object EMail {
def unapply(email: String):
Option[(String, String)] = {
val parts = email split “@”
if (parts.length == 2)
Some(parts(0), parts(1))
else
None
}
}
Extractors: How?
case class Cartesian(x: Double, y: Double)

object Polar {
def unapply(z: Cart):
Option[(Double, Double]) =
...
}

Cart(1, 1) match {
case Polar(r, th) => ...
// r == sqrt(2), th == Pi / 4
}
Extractors
• Provide user-defined views on objects
• Defined with an unapply method in a
singleton object (usually the companion
object for a class, but not necessarily so)
• Decouples types from pattern matching
• Allows pattern matching to cohabit with
data abstraction – extractor can provide
same view despite implementation changes
Functions as Objects,
Objects as Functions
Functions

• Functions in Scala:
• Inherit from the FunctionN trait
(where N is number of arguments)

• Define an apply method


• Can be subclassed in powerful ways
Functions
trait Function0[+R] {
def apply(): R
}

trait Function1[-T1, +R] {


def apply(v: T1): R
}

trait Function2[-T1, -T2, +R] {


def apply(v1: T1, v2: T2): R
}
Partial Function

trait PartialFunction[-A, +B]


extends (A => B) {
// Inherits apply from Function1
def isDefinedAt(x: A): Boolean
}
Set

trait Set[A]
extends (A => Boolean) ...
Array

class Array[A]
extends PartialFunction[Int, A] ...
Syntactic Sugar

• Any of use of obj(...) will invoke


apply(...) method on obj (even if
obj does not inherit from FunctionN)

• Useful for (among other things) factory


methods in companion objects
Apply
object EMail {
def unapply...
def apply(name: String, domain: String):
String =
name + “@” + domain
}

EMail(“jorge”, “gmail.com”)
// == “jorge@gmail.com"
Apply
case class Cartesian(x: Double, y: Double)

object Polar {
def unapply...
def apply(r: Double, th: Double):
Cartesian =
Cartesian(r * cos(th), r * sin(th))
}

Polar(sqrt(2), Pi/4)
// == Cartesian(1.0, 1.0)
Functions +
Scala Compiler
val square = (x: Int) => x*x

square(2)
Functions +
Scala Compiler
val square = (x: Int) => x*x

square(2)
Functions +
Scala Compiler
val square = new Function1[Int, Int] {
def apply(x: Int): Int = x*x
}

square(2)
Functions +
Scala Compiler
val square = new Function1[Int, Int] {
def apply(x: Int): Int = x*x
}

square(2)
Functions +
Scala Compiler
val square = new Function1[Int, Int] {
def apply(x: Int): Int = x*x
}

square.apply(2)
Functions +
Scala Compiler
val square = new Function1[Int, Int] {
def apply(x: Int): Int = x*x
}

square.apply(2)
Functions +
Scala Compiler
Function1<Integer, Integer> square =
new Function1<Integer, Integer> {
public Integer apply(Integer x) {
return x*x;
}
}

square.apply(2)
Functions +
Scala Compiler
val square = (x: Int) => x*x

square(2)
Functions +
Scala Compiler

val filter =
(c: List[A], p: A => Boolean) =>
...
Functions +
Scala Compiler

Function2<List<A>, Function1<A, Boolean>, List<A> > filter =


new Function2<List<A>, Function1<A, Boolean>, List<A> > {
public List<A> apply(List<A> c, Function1<A, Boolean> p) {
...
}
}
Lazy Programming
Evaluation Strategies

• Two evaluation strategies:


• Eager – evaluate expressions as soon as
possible
• Lazy – evaluate expressions only when
they are needed
• Most languages use eager evaluation
Eager Evaluation
val pi = {
println(“Hello, world!”)
3.14159
}

println(“Goodbye, world!”)
println(pi)

// Prints: Hello, world!


// Goodbye, world!
// 3.14159
Lazy Evaluation?
val pi = {
println(“Hello, world!”)
3.14159
}

println(“Goodbye, world!”)
println(pi)

// Prints: ???
// ???
// ???
Lazy Evaluation
lazy val pi = {
println(“Hello, world!”)
3.14159
}

println(“Goodbye, world!”)
println(pi)

// Prints: Goodbye, world!


// Hello, world!
// 3.14159
Lazy Evaluation
lazy val pi = {
println(“Hello, world!”)
3.14159
}

println(pi)
println(pi)

// Prints: ???
// ???
// ???
Lazy Evaluation
lazy val pi = {
println(“Hello, world!”)
3.14159
}

println(pi)
println(pi) // pi got cached, not
// evaluated again
// Prints: Hello, world!
// 3.14159
// 3.14159
Lazy C/C++/Java?
• C, C++, and Java have short-circuit
evaluation

• a && b
• b only gets evaluated if a is true
• a || b
• b only gets evaluated if a is false
Lazy C/C++/Java?
• C, C++, and Java also have if/then, while, for
if (condition) {
block
}

• Block only gets evaluated if condition is


true
Lazy C/C++/Java?

• In C, C++, Java, control structures (which


use a form of lazy evaluation) are defined
by the language, aren’t extensible
Lazy Scala
• Scala has two language constructs for lazy
evaluation:
• lazy vals
• thunk parameters
• These features let us define our own
control structures
while
def myWhile(cond: => Boolean)(block: => Unit): Unit = {
if (cond) {
block
myWhile(cond)(block)
}
}

var i = 0 // print: 0
myWhile(i < 10) { // 1
println(i) // 2
i = i + 1 // 3
} // ...
while
def myWhile(cond: => Boolean)(block: => Unit): Unit = {
if (cond) {
block
myWhile(cond)(block)
}
}

var i = 11
myWhile(i < 10) {
println(i) // prints nothing
i = i + 1
}

You might also like