Practically Functional

77
Practically Functional Daniel Spiewak

description

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.

Transcript of Practically Functional

  • 1.Practically Functional Daniel Spiewak

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. 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. Definitions Q: What is functional programming? 5. Definitions Q: What is functional programming? A: Nobody knows! 6. Definitions Q: What is purely-functional? 7. Definitions Q: What is purely-functional? Everything is immutable (no variables) 8. Definitions Q: What is purely-functional? Everything is immutable (no variables) Absolutely no side-effects println(quot;Hello, World!quot;) 9. Definitions Q: What is purely-functional? Everything is immutable (no variables) Absolutely no side-effects Referential transparency 10. Definitions Q: What is purely-functional? Everything is immutable (no variables) Absolutely no side-effects Referential transparency Bondage discipline? 11. Definitions Scala is not purely-functional vars Mutable collections Uncontrolled side-effects (println) 12. Definitions Scala is not purely-functional vars Mutable collections Uncontrolled side-effects (println) Is Scala a functional language? 13. Functional Trademarks Higher-order functionsdef foreach(f: String=>Unit) { f(quot;Whatquot;) f(quot;isquot;) f(quot;goingquot;) f(quot;on?quot;) } 14. Functional Trademarks Higher-order functionsforeach { s => println(s) } 15. Functional Trademarks Higher-order functions Closures are anonymous functions Ruby, Groovy, Python; none of these count!foreach(println) 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. 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-inferenceval me = quot;Danielquot; // equivalent to... val me: String = quot;Danielquot; 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-inferenceforeach { s => println(s) } 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. What does this buy you? Modularity (separation of concerns) Understandability No more spooky action at a distance 21. What 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); } } } 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. 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. Functional Idioms Recursion instead of loops Scala helps with this by allowing methods within methods 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 null }// uninteresting and ugly def readFile(file: String): Map[String, String] = { import collection.jcl.Hashtabletry { val is = new BufferedInputStream(new FileInputStream(file)) val p = new Propertiesp.load(is) is.close()new Hashtable(p).asInstanceOf[Hashtable[String, String]] } catch { case _ => null } } 34. import collection.mutable.ListBufferdef 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(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 nullif (back != null) back :: readPeople(tail) else readPeople(tail) }case Nil => Nil } 37. Example #1 Loops Recursion Higher-order functions 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 nullif (back != null) back :: people else people } } 39. Example #1 Loops Recursion Higher-order functions Monads! 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.Hashtabletry { val is = new BufferedInputStream(new FileInputStream(file)) val p = new Propertiesp.load(is) is.close()Some(new Hashtable(p).asInstanceOf[Hashtable[String, String]]) } catch { case _ => None } } 41. def readPeople(files: List[String]): List[Person] = { for {file =). More later 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. Other Common Util Methods filter (used in for-comprehensions)val nums = List(1, 2, 3, 4, 5) nums filter { _ % 2 == 0 } 54. Other Common Util Methods filter (used in for-comprehensions)val nums = List(1, 2, 3, 4, 5) nums filter (0 == 2 %) 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. Other Common Util Methods filter (used in for-comprehensions) zip / zipWithIndex zipWith (not available pre-2.8.0) forall and existsval nums = List(1, 2, 3, 4, 5) nums forall { _ % 2 == 0 } // => false 57. Other Common Util Methods filter (used in for-comprehensions) zip / zipWithIndex zipWith (not available pre-2.8.0) forall and existsval nums = List(1, 2, 3, 4, 5) nums exists { _ % 2 == 0 } // => true 58. Other Common Util Methods filter (used in for-comprehensions) zip / zipWithIndex zipWith (not available pre-2.8.0) forall and exists take and dropval nums = List(5, 4, 3, 2, 1) nums take 2 // => List(5, 4) 59. Other Common Util Methods filter (used in for-comprehensions) zip / zipWithIndex zipWith (not available pre-2.8.0) forall and exists take and dropval nums = List(5, 4, 3, 2, 1) nums drop 2 // => List(3, 2, 1) 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 foreachval names = List(quot;Danielquot;, quot;Chrisquot;) names foreach println 61. Example #2 Comparing the prefix of a List[Char]to a given string.List[Char] StringResult 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. def isPrefix(chars: List[Char], str: String) = { if (chars.lengthCompare(str.length) < 0) { false } else { val trunc = chars take str.lengthtrunc.zipWithIndex forall { case (c, i) => c == str(i) } } } 63. More About Combinators The Essence of Functional Programming Combine simple things to solve complex problems Very high level Think about Lego bricks 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. 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. More About Combinators Three Types of Combinators Sequential (first a, then b) a ~ b 67. More About Combinators Three Types of Combinators Sequential (first a, then b) Disjunctive (either a, or b) a | b 68. More About Combinators Three Types of Combinators Sequential (first a, then b) Disjunctive (either a, or b) Literal (exactly foo) quot;fooquot; 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. More About Combinators Seldom created, often used Good for problems which split into smaller sub-problems 71. March of the Monads Monads are not scary 72. March of the Monads Monads are not scary Monad explanations are scary 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. March of the Monads All monads have A type constructorclass Option[A] { } A single-argument constructornew Some(quot;one to watch over mequot;) A flatMap method which behaves properlya flatMap { v => v.next } 75. March of the Monads 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. 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)