whoami
Author of Scala for Java Refugees
and other articles on Scala and FP
Former editor Javalobby / EclipseZone
Engaged in academic research
involving Scala DSLs and text parsing
(ScalaBison, GLL
Combinators, ScalaQL)
Agenda
Define “functional programming” (sort
of)
See some common elements of FP
Motivate why this stuff is useful in the
real world (hopefully)
Show practical functional techniques
and design patterns
Explain monads!
Hopefully pique your interest in
learning and applying more of this
Definitions
Q: What is “functional programming”?
Definitions
Q: What is “functional programming”?
◦ A: Nobody knows!
Definitions
Q: What is “purely-functional”?
Definitions
Q: What is “purely-functional”?
◦ Everything is immutable (no variables)
Definitions
Q: What is “purely-functional”?
◦ Everything is immutable (no variables)
◦ Absolutely no side-effects
println(\"Hello, World!\")
Definitions
Q: What is “purely-functional”?
◦ Everything is immutable (no variables)
◦ Absolutely no side-effects
◦ Referential transparency
Definitions
Q: What is “purely-functional”?
◦ Everything is immutable (no variables)
◦ Absolutely no side-effects
◦ Referential transparency
◦ Bondage discipline?
Definitions
Scala is not purely-functional
◦ vars
◦ Mutable collections
◦ Uncontrolled side-effects (println)
Definitions
Scala is not purely-functional
◦ vars
◦ Mutable collections
◦ Uncontrolled side-effects (println)
Is Scala a “functional language”?
Functional Trademarks
Higher-order functions
Closures are anonymous functions
◦ Ruby, Groovy, Python; none of these
count!
foreach(println)
Functional Trademarks
Higher-order functions
Closures are anonymous functions
◦ Ruby, Groovy, Python; none of these
count!
Singly-linked immutable lists (cons
cells)
val names = \"Chris\" :: \"Joe\" :: Nil
val names2 = \"Daniel\" :: names
Functional Trademarks
Higher-order functions
Closures are anonymous functions
◦ Ruby, Groovy, Python; none of these
count!
Singly-linked immutable lists (cons
cells)
Usually some form of type-inference
val me = \"Daniel\"
// equivalent to...
val me: String = \"Daniel\"
Functional Trademarks
Higher-order functions
Closures are anonymous functions
◦ Ruby, Groovy, Python; none of these
count!
Singly-linked immutable lists (cons
cells)
Usually some form of type-inference
foreach { s => println(s) }
Functional Trademarks
Higher-order functions
Closures are anonymous functions
◦ Ruby, Groovy, Python; none of these
count!
Singly-linked immutable lists (cons
cells)
Usually some form of type-inference
Immutable by default (or encouraged)
val me = \"Daniel\"
var me = \"Daniel\"
What does this buy you?
Modularity (separation of concerns)
Understandability
No more “spooky action at a distance”
…
What does this buy you?
public class Company {
private List<Person> employees;
public List<Person> getEmployees() {
return employees;
}
public void addEmployee(Person p) {
if (p.isAlive()) {
employees.add(p);
}
}
}
What does this buy you?
Modularity (separation of concerns)
Understandability
No more “spooky action at a distance”
Flexible libraries (more on this later)
Syntactic power (internal DSLs)
What does this buy you?
\"vector\" should {
\"store a single element\" in {
val prop = forAll { (i: Int, e: Int) =>
i >= 0 ==> { (vector(0) = e)(0) mustEqual e }
}
prop must pass
}
\"implement length\" in {
val prop = forAll { list: List[Int] =>
val vec = Vector(list:_*)
vec.length mustEqual list.length
}
prop must pass
}
}
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
def factorial(n: Int) = {
var back = 1
for (i <- 1 to n) {
back *= i
}
back
}
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
def factorial(n: Int): Int = {
if (n == 1)
1
else
n * factorial(n - 1)
}
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
def factorial(n: Int) = {
def loop(n: Int, acc: Int): Int = {
if (n == 1)
acc
else
loop(n - 1, acc * n)
}
loop(n, 1)
}
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
Higher-order functions instead of
recursion
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
Higher-order functions instead of
recursion
Combinators instead of higher-order
functions
Functional Idioms
Recursion instead of loops
◦ Scala helps with this by allowing methods
within methods
Higher-order functions instead of
recursion
Combinators instead of higher-order
functions
Monads!
Example #1
Retrieve structured, formatted data from
across multiple .properties files
and multiple keys within those files.
# daniel.properties # tim.properties
name.first = Daniel name.first = Timothy
name.last = Spiewak name.last = Olmsted
age = 21 age = 22
Example #1
Using loops
def toInt(s: String) = try {
s.toInt
} catch {
case _ => null
}
// uninteresting and ugly
def readFile(file: String): Map[String, String] = {
import collection.jcl.Hashtable
try {
val is = new BufferedInputStream(new FileInputStream(file))
val p = new Properties
p.load(is)
is.close()
new Hashtable(p).asInstanceOf[Hashtable[String, String]]
} catch {
case _ => null
}
}
import collection.mutable.ListBuffer
def readPeople(files: List[String]): List[Person] = {
val back = new ListBuffer[Person]
for (file <- files) {
val props = readFile(file)
if (props != null) {
if (props.contains(\"name.first\") &&
props.contains(\"name.last\") &&
props.contains(\"age\")) {
val age = toInt(props(\"age\"))
if (age != null)
back += new Person(props(\"name.first\"),
props(\"name.last\"), age)
}
}
}
back.toList
}
Example #1
Using loops
Recursive
def readPeople(files: List[String]): List[Person] = files match {
case file :: tail => {
val props = readFile(file)
val back = if (props != null) {
if (props.contains(\"name.first\") &&
props.contains(\"name.last\") &&
props.contains(\"age\")) {
val age = toInt(props(\"age\"))
if (age != null)
new Person(props(\"name.first\"), props(\"name.last\"), age)
else
null
} else null
} else null
if (back != null)
back :: readPeople(tail)
else
readPeople(tail)
}
case Nil => Nil
}
Example #1
Loops
Recursion
Higher-order functions
def readPeople(files: List[String]): List[Person] = {
files.foldRight(List[String]()) { (file, people) =>
val props = readFile(file)
val back = if (props != null) {
if (props.contains(\"name.first\") &&
props.contains(\"name.last\") &&
props.contains(\"age\")) {
val age = toInt(props(\"age\"))
if (age != null)
new Person(props(\"name.first\"), props(\"name.last\"), age)
else
null
} else null
} else null
if (back != null)
back :: people
else
people
}
}
def readPeople(files: List[String]) = {
import Function._
files flatMap readFile flatMap { props =>
val fNames = props get \"name.first\"
val names = fNames flatMap {
(_, props get \"name.last\")
}
val data = names flatMap {
case (fn, ln) =>
(fn, ln, props get \"age\" map toInt)
}
data map tupled(new Person _)
}
}
What did we just see?
foldLeft / foldRight
◦ Catamorphisms
◦ Use when you want to reduce all of the
elements of a collection into a single
result
◦ Capable of almost anything!
What did we just see?
foldLeft / foldRight
def sum(nums: List[Int]) = {
nums.foldLeft(0) { (x, y) =>
x + y
}
}
What did we just see?
foldLeft / foldRight
def sum(nums: List[Int]) = {
nums.foldLeft(0) { _ + _ }
}
What did we just see?
foldLeft / foldRight
def sum(nums: List[Int]) = {
(0 /: nums) { _ + _ }
}
What did we just see?
foldLeft / foldRight
map
◦ Use when you want to transform every
element of a collection, leaving the results
in the corresponding location within a new
collection
What did we just see?
foldLeft / foldRight
map
val nums = List(\"1\", \"2\", \"3\", \"4\", \"5\")
nums map { str => str.toInt }
What did we just see?
foldLeft / foldRight
map
val nums = List(\"1\", \"2\", \"3\", \"4\", \"5\")
nums map { _.toInt }
What did we just see?
foldLeft / foldRight
map
flatMap (has two meanings)
◦ Collections: Use when you want to
transform every element optionally
◦ Monads: Should have really been called
“bind” (or >>=). More later…
What did we just see?
foldLeft / foldRight
map
flatMap (has two meanings)
def toCharArray(arr: Array[String]) = {
arr flatMap { _.toCharArray }
}
toCharArray(Array(\"Daniel\", \"Spiewak\"))
// =>
Array('D', 'a', 'n', 'i', 'e', 'l',
'S', 'p', 'i', 'e', 'w', 'a', 'k')
Other Common Util Methods
filter (used in for-comprehensions)
val nums = List(1, 2, 3, 4, 5)
nums filter { _ % 2 == 0 }
Other Common Util Methods
filter (used in for-comprehensions)
val nums = List(1, 2, 3, 4, 5)
nums filter (0 == 2 %)
Other Common Util Methods
filter (used in for-comprehensions)
zip / zipWithIndex
◦ zipWith (not available pre-2.8.0)
val evens = List(2, 4, 6)
val odds = List(1, 3, 5)
evens zip odds
// =>
List((1, 2), (3, 4), (5, 6))
Other Common Util Methods
filter (used in for-comprehensions)
zip / zipWithIndex
◦ zipWith (not available pre-2.8.0)
forall and exists
val nums = List(1, 2, 3, 4, 5)
nums forall { _ % 2 == 0 } // => false
Other Common Util Methods
filter (used in for-comprehensions)
zip / zipWithIndex
◦ zipWith (not available pre-2.8.0)
forall and exists
val nums = List(1, 2, 3, 4, 5)
nums exists { _ % 2 == 0 } // => true
Other Common Util Methods
filter (used in for-comprehensions)
zip / zipWithIndex
◦ zipWith (not available pre-2.8.0)
forall and exists
take and drop
val nums = List(5, 4, 3, 2, 1)
nums take 2
// =>
List(5, 4)
Other Common Util Methods
filter (used in for-comprehensions)
zip / zipWithIndex
◦ zipWith (not available pre-2.8.0)
forall and exists
take and drop
val nums = List(5, 4, 3, 2, 1)
nums drop 2
// =>
List(3, 2, 1)
Other Common Util Methods
filter (used in for-comprehensions)
zip / zipWithIndex
◦ zipWith (not available pre-2.8.0)
forall and exists
take and drop
foreach
val names = List(\"Daniel\", \"Chris\")
names foreach println
Example #2
Comparing the prefix of a List[Char]
to a given string.
List[Char] String Result
List('d', 'a', 'n', 'i', 'e', 'l') \"dan\" true
List('d', 'a', 'n', 'i', 'e', 'l') \"iel\" false
List('t', 'e', 's', 't') \"testing\" false
List('t', 'e', 's', 't') \"test\" true
def isPrefix(chars: List[Char], str: String) = {
if (chars.lengthCompare(str.length) < 0) {
false
} else {
val trunc = chars take str.length
trunc.zipWithIndex forall {
case (c, i) => c == str(i)
}
}
}
More About Combinators
The “Essence of Functional
Programming”
Combine simple things to solve
complex problems
Very high level
Think about Lego™ bricks
More About Combinators
The best example: Parser
Combinators
def expr: Parser[Int] = (
num ~ \"+\" ~ expr ^^ { case (x, _, y) => x + y }
| num ~ \"-\" ~ expr ^^ { case (x, _, y) => x - y }
| num
)
def num = \"\"\"\\d+\"\"\".r ^^ { _.toInt }
More About Combinators
The best example: Parser
Combinators
def expr: Parser[Int] = (
num ~ \"+\" ~ expr ^^ { case (x, _, y) => x + y }
| num ~ \"-\" ~ expr ^^ { case (x, _, y) => x - y }
| num
)
def num = \"\"\"\\d+\"\"\".r ^^ { _.toInt }
expr(\"12 + 7 - 4\") // => Success(15)
expr(\"42\") // => Success(42)
More About Combinators
Three Types of Combinators
◦ Sequential (first a, then b)
a ~ b
More About Combinators
Three Types of Combinators
◦ Sequential (first a, then b)
◦ Disjunctive (either a, or b)
a | b
More About Combinators
Three Types of Combinators
◦ Sequential (first a, then b)
◦ Disjunctive (either a, or b)
◦ Literal (exactly foo)
\"foo\"
More About Combinators
Three Types of Combinators
◦ Sequential (first a, then b)
◦ Disjunctive (either a, or b)
◦ Literal (exactly foo)
Note: Our example uses a regular
expression parser, but that is only a
generalization of the literal parser
More About Combinators
Seldom created, often used
Good for problems which split into
smaller sub-problems
March of the Monads
Monads are not scary
March of the Monads
Monads are not scary
Monad explanations are scary
March of the Monads
Monads are little containers for
encapsulating something
◦ What the “something” is depends on the
monad
An instance of a monad can be
“bound” together with another instance
of that monad
Most combinators are monads
March of the Monads
All monads have
◦ A type constructor
class Option[A] { … }
◦ A single-argument constructor
new Some(\"one to watch over me\")
◦ A flatMap method which behaves
properly
a flatMap { v => v.next }
March of the Monads
March of the Monads
Option
◦ This is what the Groovy folks really wanted
when they designed the “Elvis Operator”
Parser
◦ Sequential parser is really two bound
parsers
◦ Disjunctive parser uses an optional
monadic function: orElse
◦ Literal parser is the one-argument
constructor
Function1 (sort of)
◦ We could say that function composition is
Learn More
Read my blog! :-)
◦ http://www.codecommit.com/blog
Some better sources…
◦ http://apocalisp.wordpress.com/
◦ http://michid.wordpress.com/
◦ http://joelneely.wordpress.com/
◦ http://scala-blogs.org
A really good paper…
◦ Monadic Parser Combinators
(1996; Hutton, Meijer)
Slides for my recent presentation at the CASE meetu more
Slides for my recent presentation at the CASE meetup, May 21st. Discusses functional programming features in Scala. Goes from basic FP features like higher-order functions all the way through to monads. less
1 comments
Comments 1 - 1 of 1 previous next Post a comment