Practically Functional

77
Practically Functional Daniel Spiewak

description

Practically Functional. Daniel Spiewak. 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. - PowerPoint PPT Presentation

Transcript of Practically Functional

Make Your Words Eloquent and Your Programs Functional

Practically FunctionalDaniel Spiewak

whoamiAuthor of Scala for Java Refugees and other articles on Scala and FPFormer editor Javalobby / EclipseZoneEngaged in academic research involving Scala DSLs and text parsing (ScalaBison, GLL Combinators, ScalaQL)AgendaDefine functional programming (sort of)See some common elements of FPMotivate why this stuff is useful in the real world (hopefully)Show practical functional techniques and design patternsExplain monads!Hopefully pique your interest in learning and applying more of this stuffDefinitionsQ: What is functional programming?DefinitionsQ: What is functional programming?A: Nobody knows!DefinitionsQ: What is purely-functional?DefinitionsQ: What is purely-functional?Everything is immutable (no variables)DefinitionsQ: What is purely-functional?Everything is immutable (no variables)Absolutely no side-effects

println("Hello, World!")DefinitionsQ: What is purely-functional?Everything is immutable (no variables)Absolutely no side-effectsReferential transparencyDefinitionsQ: What is purely-functional?Everything is immutable (no variables)Absolutely no side-effectsReferential transparencyBondage discipline?DefinitionsScala is not purely-functionalvarsMutable collectionsUncontrolled side-effects (println)DefinitionsScala is not purely-functionalvarsMutable collectionsUncontrolled side-effects (println)Is Scala a functional language?Functional TrademarksHigher-order functions

def foreach(f: String=>Unit) { f("What") f("is") f("going") f("on?")}Functional TrademarksHigher-order functions

foreach { s => println(s) }Functional TrademarksHigher-order functionsClosures are anonymous functionsRuby, Groovy, Python; none of these count!

foreach(println)Functional TrademarksHigher-order functionsClosures are anonymous functionsRuby, Groovy, Python; none of these count!Singly-linked immutable lists (cons cells)

val names = "Chris" :: "Joe" :: Nilval names2 = "Daniel" :: namesFunctional TrademarksHigher-order functionsClosures are anonymous functionsRuby, 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 TrademarksHigher-order functionsClosures are anonymous functionsRuby, Groovy, Python; none of these count!Singly-linked immutable lists (cons cells)Usually some form of type-inference

foreach { s => println(s) }Functional TrademarksHigher-order functionsClosures are anonymous functionsRuby, Groovy, Python; none of these count!Singly-linked immutable lists (cons cells)Usually some form of type-inferenceImmutable by default (or encouraged)

val me = "Daniel"var me = "Daniel"What does this buy you?Modularity (separation of concerns)UnderstandabilityNo more spooky action at a distanceWhat does this buy you?public class Company { private List employees;

public List getEmployees() { return employees; }

public void addEmployee(Person p) { if (p.isAlive()) { employees.add(p); } }}What does this buy you?Modularity (separation of concerns)UnderstandabilityNo more spooky action at a distanceFlexible 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 IdiomsRecursion instead of loopsScala helps with this by allowing methods within methodsFunctional IdiomsRecursion instead of loopsScala helps with this by allowing methods within methods

def factorial(n: Int) = { var back = 1 for (i null}

// uninteresting and uglydef 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 { 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 #1LoopsRecursionHigher-order functionsdef 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 }}Example #1LoopsRecursionHigher-order functionsMonads!def toInt(s: String) = try { Some(s.toInt)} catch { case _ => None}

// uninteresting and uglydef readFile(file: String): Option[Map[String, String]] = { import collection.jcl.Hashtable

try { val is = new BufferedInputStream(new FileInputStream(file)) val p = new Properties p.load(is) is.close() Some(new Hashtable(p).asInstanceOf[Hashtable[String, String]]) } catch { case _ => None }}def readPeople(files: List[String]): List[Person] = { for { file =). More laterWhat did we just see?foldLeft / foldRightmapflatMap (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 Methodsfilter (used in for-comprehensions)

val nums = List(1, 2, 3, 4, 5)nums filter { _ % 2 == 0 }Other Common Util Methodsfilter (used in for-comprehensions)

val nums = List(1, 2, 3, 4, 5)nums filter (0 == 2 %)Other Common Util Methodsfilter (used in for-comprehensions)zip / zipWithIndexzipWith (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 Methodsfilter (used in for-comprehensions)zip / zipWithIndexzipWith (not available pre-2.8.0)forall and exists

val nums = List(1, 2, 3, 4, 5)nums forall { _ % 2 == 0 } // => falseOther Common Util Methodsfilter (used in for-comprehensions)zip / zipWithIndexzipWith (not available pre-2.8.0)forall and exists

val nums = List(1, 2, 3, 4, 5)nums exists { _ % 2 == 0 } // => trueOther Common Util Methodsfilter (used in for-comprehensions)zip / zipWithIndexzipWith (not available pre-2.8.0)forall and existstake and drop

val nums = List(5, 4, 3, 2, 1)nums take 2// =>List(5, 4)Other Common Util Methodsfilter (used in for-comprehensions)zip / zipWithIndexzipWith (not available pre-2.8.0)forall and existstake and drop

val nums = List(5, 4, 3, 2, 1)nums drop 2// =>List(3, 2, 1)Other Common Util Methodsfilter (used in for-comprehensions)zip / zipWithIndexzipWith (not available pre-2.8.0)forall and existstake and dropforeach

val names = List("Daniel", "Chris")names foreach printlnExample #2Comparing the prefix of a List[Char] to a given string.

List[Char]StringResultList('d', 'a', 'n', 'i', 'e', 'l')"dan"trueList('d', 'a', 'n', 'i', 'e', 'l')"iel"falseList('t', 'e', 's', 't')"testing"falseList('t', 'e', 's', 't')"test"truedef 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 CombinatorsThe Essence of Functional ProgrammingCombine simple things to solve complex problemsVery high levelThink about Lego bricks

More About CombinatorsThe 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 CombinatorsThe 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 CombinatorsThree Types of CombinatorsSequential (first a, then b)

a ~ bMore About CombinatorsThree Types of CombinatorsSequential (first a, then b)Disjunctive (either a, or b)

a | bMore About CombinatorsThree Types of CombinatorsSequential (first a, then b)Disjunctive (either a, or b)Literal (exactly foo)

"foo"More About CombinatorsThree Types of CombinatorsSequential (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 parserMore About CombinatorsSeldom created, often usedGood for problems which split into smaller sub-problemsMarch of the MonadsMonads are not scaryMarch of the MonadsMonads are not scaryMonad explanations are scaryMarch of the MonadsMonads are little containers for encapsulating somethingWhat the something is depends on the monadAn instance of a monad can be bound together with another instance of that monadMost combinators are monadsMarch of the MonadsAll monads haveA type constructorclass Option[A] { }A single-argument constructornew Some("one to watch over me")A flatMap method which behaves properlya flatMap { v => v.next }March of the Monads

March of the MonadsOptionThis is what the Groovy folks really wanted when they designed the Elvis OperatorParserSequential parser is really two bound parsersDisjunctive parser uses an optional monadic function: orElseLiteral parser is the one-argument constructorFunction1 (sort of)We could say that function composition is like a bind operation, but Scala didnt do thatLearn MoreRead my blog! :-)http://www.codecommit.com/blogSome better sourceshttp://apocalisp.wordpress.com/http://michid.wordpress.com/http://joelneely.wordpress.com/http://scala-blogs.orgA really good paperMonadic Parser Combinators (1996; Hutton, Meijer)