Practically Functional

3,310 views

Published on

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.

Published in: Technology
2 Comments
5 Likes
Statistics
Notes
No Downloads
Views
Total views
3,310
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
168
Comments
2
Likes
5
Embeds 0
No embeds

No notes for slide

Practically Functional

  1. 1. Practically Functional Daniel Spiewak
  2. 2. 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)
  3. 3. 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
  4. 4. Definitions  Q: What is “functional programming”?
  5. 5. Definitions  Q: What is “functional programming”? ◦ A: Nobody knows!
  6. 6. Definitions  Q: What is “purely-functional”?
  7. 7. Definitions  Q: What is “purely-functional”? ◦ Everything is immutable (no variables)
  8. 8. Definitions  Q: What is “purely-functional”? ◦ Everything is immutable (no variables) ◦ Absolutely no side-effects println(quot;Hello, World!quot;)
  9. 9. Definitions  Q: What is “purely-functional”? ◦ Everything is immutable (no variables) ◦ Absolutely no side-effects ◦ Referential transparency
  10. 10. Definitions  Q: What is “purely-functional”? ◦ Everything is immutable (no variables) ◦ Absolutely no side-effects ◦ Referential transparency ◦ Bondage discipline?
  11. 11. Definitions  Scala is not purely-functional ◦ vars ◦ Mutable collections ◦ Uncontrolled side-effects (println)
  12. 12. Definitions  Scala is not purely-functional ◦ vars ◦ Mutable collections ◦ Uncontrolled side-effects (println)  Is Scala a “functional language”?
  13. 13. Functional Trademarks  Higher-order functions def foreach(f: String=>Unit) { f(quot;Whatquot;) f(quot;isquot;) f(quot;goingquot;) f(quot;on?quot;) }
  14. 14. Functional Trademarks  Higher-order functions foreach { s => println(s) }
  15. 15. Functional Trademarks  Higher-order functions  Closures are anonymous functions ◦ Ruby, Groovy, Python; none of these count! foreach(println)
  16. 16. Functional Trademarks  Higher-order functions  Closures are anonymous functions ◦ Ruby, Groovy, Python; none of these count!  Singly-linked immutable lists (cons cells) val names = quot;Chrisquot; :: quot;Joequot; :: Nil val names2 = quot;Danielquot; :: names
  17. 17. 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 = quot;Danielquot; // equivalent to... val me: String = quot;Danielquot;
  18. 18. 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) }
  19. 19. 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 = quot;Danielquot; var me = quot;Danielquot;
  20. 20. What does this buy you?  Modularity (separation of concerns)  Understandability  No more “spooky action at a distance” …
  21. 21. 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); } } }
  22. 22. 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)
  23. 23. What does this buy you? quot;vectorquot; should { quot;store a single elementquot; in { val prop = forAll { (i: Int, e: Int) => i >= 0 ==> { (vector(0) = e)(0) mustEqual e } } prop must pass } quot;implement lengthquot; in { val prop = forAll { list: List[Int] => val vec = Vector(list:_*) vec.length mustEqual list.length } prop must pass } }
  24. 24. Functional Idioms  Recursion instead of loops ◦ Scala helps with this by allowing methods within methods
  25. 25. 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 }
  26. 26. 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) }
  27. 27. 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) }
  28. 28. Functional Idioms  Recursion instead of loops ◦ Scala helps with this by allowing methods within methods  Higher-order functions instead of recursion
  29. 29. 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
  30. 30. 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!
  31. 31. 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
  32. 32. Example #1  Using loops
  33. 33. 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 } }
  34. 34. 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(quot;name.firstquot;) && props.contains(quot;name.lastquot;) && props.contains(quot;agequot;)) { val age = toInt(props(quot;agequot;)) if (age != null) back += new Person(props(quot;name.firstquot;), props(quot;name.lastquot;), age) } } } back.toList }
  35. 35. Example #1  Using loops  Recursive
  36. 36. def readPeople(files: List[String]): List[Person] = files match { case file :: tail => { val props = readFile(file) val back = if (props != null) { if (props.contains(quot;name.firstquot;) && props.contains(quot;name.lastquot;) && props.contains(quot;agequot;)) { val age = toInt(props(quot;agequot;)) if (age != null) new Person(props(quot;name.firstquot;), props(quot;name.lastquot;), age) else null } else null } else null if (back != null) back :: readPeople(tail) else readPeople(tail) } case Nil => Nil }
  37. 37. Example #1  Loops  Recursion  Higher-order functions
  38. 38. 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(quot;name.firstquot;) && props.contains(quot;name.lastquot;) && props.contains(quot;agequot;)) { val age = toInt(props(quot;agequot;)) if (age != null) new Person(props(quot;name.firstquot;), props(quot;name.lastquot;), age) else null } else null } else null if (back != null) back :: people else people } }
  39. 39. Example #1  Loops  Recursion  Higher-order functions …  Monads!
  40. 40. def toInt(s: String) = try { Some(s.toInt) } catch { case _ => None } // uninteresting and ugly def 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 } }
  41. 41. def readPeople(files: List[String]): List[Person] = { for { file <- files props <- readFile(file) firstName <- props get quot;name.firstquot; lastName <- props get quot;name.lastquot; ageString <- props get quot;agequot; age <- toInt(ageString) } yield new Person(firstName, lastName, age) }
  42. 42. Example #1  Loops  Recursion  Higher-order functions  Combinators  Monads!
  43. 43. def readPeople(files: List[String]) = { import Function._ files flatMap readFile flatMap { props => val fNames = props get quot;name.firstquot; val names = fNames flatMap { (_, props get quot;name.lastquot;) } val data = names flatMap { case (fn, ln) => (fn, ln, props get quot;agequot; map toInt) } data map tupled(new Person _) } }
  44. 44. 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!
  45. 45. What did we just see?  foldLeft / foldRight def sum(nums: List[Int]) = { nums.foldLeft(0) { (x, y) => x + y } }
  46. 46. What did we just see?  foldLeft / foldRight def sum(nums: List[Int]) = { nums.foldLeft(0) { _ + _ } }
  47. 47. What did we just see?  foldLeft / foldRight def sum(nums: List[Int]) = { (0 /: nums) { _ + _ } }
  48. 48. 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
  49. 49. What did we just see?  foldLeft / foldRight  map val nums = List(quot;1quot;, quot;2quot;, quot;3quot;, quot;4quot;, quot;5quot;) nums map { str => str.toInt }
  50. 50. What did we just see?  foldLeft / foldRight  map val nums = List(quot;1quot;, quot;2quot;, quot;3quot;, quot;4quot;, quot;5quot;) nums map { _.toInt }
  51. 51. 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…
  52. 52. What did we just see?  foldLeft / foldRight  map  flatMap (has two meanings) def toCharArray(arr: Array[String]) = { arr flatMap { _.toCharArray } } toCharArray(Array(quot;Danielquot;, quot;Spiewakquot;)) // => Array('D', 'a', 'n', 'i', 'e', 'l', 'S', 'p', 'i', 'e', 'w', 'a', 'k')
  53. 53. Other Common Util Methods  filter (used in for-comprehensions) val nums = List(1, 2, 3, 4, 5) nums filter { _ % 2 == 0 }
  54. 54. Other Common Util Methods  filter (used in for-comprehensions) val nums = List(1, 2, 3, 4, 5) nums filter (0 == 2 %)
  55. 55. 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))
  56. 56. 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
  57. 57. 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
  58. 58. 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)
  59. 59. 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)
  60. 60. 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(quot;Danielquot;, quot;Chrisquot;) names foreach println
  61. 61. Example #2 Comparing the prefix of a List[Char] to a given string. List[Char] String Result List('d', 'a', 'n', 'i', 'e', 'l') quot;danquot; true List('d', 'a', 'n', 'i', 'e', 'l') quot;ielquot; false List('t', 'e', 's', 't') quot;testingquot; false List('t', 'e', 's', 't') quot;testquot; true
  62. 62. 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) } } }
  63. 63. More About Combinators  The “Essence of Functional Programming”  Combine simple things to solve complex problems  Very high level  Think about Lego™ bricks
  64. 64. More About Combinators  The best example: Parser Combinators def expr: Parser[Int] = ( num ~ quot;+quot; ~ expr ^^ { case (x, _, y) => x + y } | num ~ quot;-quot; ~ expr ^^ { case (x, _, y) => x - y } | num ) def num = quot;quot;quot;d+quot;quot;quot;.r ^^ { _.toInt }
  65. 65. More About Combinators  The best example: Parser Combinators def expr: Parser[Int] = ( num ~ quot;+quot; ~ expr ^^ { case (x, _, y) => x + y } | num ~ quot;-quot; ~ expr ^^ { case (x, _, y) => x - y } | num ) def num = quot;quot;quot;d+quot;quot;quot;.r ^^ { _.toInt } expr(quot;12 + 7 - 4quot;) // => Success(15) expr(quot;42quot;) // => Success(42)
  66. 66. More About Combinators  Three Types of Combinators ◦ Sequential (first a, then b) a ~ b
  67. 67. More About Combinators  Three Types of Combinators ◦ Sequential (first a, then b) ◦ Disjunctive (either a, or b) a | b
  68. 68. More About Combinators  Three Types of Combinators ◦ Sequential (first a, then b) ◦ Disjunctive (either a, or b) ◦ Literal (exactly foo) quot;fooquot;
  69. 69. 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
  70. 70. More About Combinators  Seldom created, often used  Good for problems which split into smaller sub-problems
  71. 71. March of the Monads  Monads are not scary
  72. 72. March of the Monads  Monads are not scary  Monad explanations are scary
  73. 73. 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
  74. 74. March of the Monads  All monads have ◦ A type constructor class Option[A] { … } ◦ A single-argument constructor new Some(quot;one to watch over mequot;) ◦ A flatMap method which behaves properly a flatMap { v => v.next }
  75. 75. March of the Monads
  76. 76. 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
  77. 77. 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)

×