Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A (Somewhat Gentle) Introduction to Functional Programming

A (Somewhat Gentle) Introduction to Functional Programming

A short talk I gave recently introducing Functional Programming.

Some of the discussion was done free form hence not 100% "web friendly" slides, but feel free to forward questions to me.

Brendan McAdams

March 03, 2012
Tweet

More Decks by Brendan McAdams

Other Decks in Programming

Transcript

  1. Brendan McAdams 10gen, Inc. [email protected] @rit “Functions All The Way

    Down” A (Somewhat Gentle) Introduction to Functional Programming
  2. Everything That’s Old Is New Again... • Functional Programming is

    far from a new concept • Rooted in the concepts of Lambda Calculus (~1930s)
  3. Everything That’s Old Is New Again... • Functional Programming is

    far from a new concept • Rooted in the concepts of Lambda Calculus (~1930s) • Introduced in many early languages such as Lisp
  4. Everything That’s Old Is New Again... • Functional Programming is

    far from a new concept • Rooted in the concepts of Lambda Calculus (~1930s) • Introduced in many early languages such as Lisp • Recently undergoing a renaissance
  5. Everything That’s Old Is New Again... • Functional Programming is

    far from a new concept • Rooted in the concepts of Lambda Calculus (~1930s) • Introduced in many early languages such as Lisp • Recently undergoing a renaissance • Functional Languages like Haskell, Clojure and Scala gaining popularity as “real” tools
  6. Everything That’s Old Is New Again... • Functional Programming is

    far from a new concept • Rooted in the concepts of Lambda Calculus (~1930s) • Introduced in many early languages such as Lisp • Recently undergoing a renaissance • Functional Languages like Haskell, Clojure and Scala gaining popularity as “real” tools • Traditionally imperative languages such as C++ are gaining concepts from functional languages in recent revisions
  7. Everything That’s Old Is New Again... • We improve ourselves

    and our code by learning from & leveraging FP
  8. Everything That’s Old Is New Again... • We improve ourselves

    and our code by learning from & leveraging FP • Avoiding Mutable values and State for referential transparency
  9. Everything That’s Old Is New Again... • We improve ourselves

    and our code by learning from & leveraging FP • Avoiding Mutable values and State for referential transparency • Using functions as a higher level concept for composition
  10. Everything That’s Old Is New Again... • We improve ourselves

    and our code by learning from & leveraging FP • Avoiding Mutable values and State for referential transparency • Using functions as a higher level concept for composition • ... and thinking in terms of functions being closer to the composable, reusable units they are meant to be
  11. Everything That’s Old Is New Again... • I am going

    to introduce some functional programming (as well as a little bit of my favorite language for it, Scala)
  12. Everything That’s Old Is New Again... • I am going

    to introduce some functional programming (as well as a little bit of my favorite language for it, Scala) • This is by no means a comprehensive course on functional programming
  13. Everything That’s Old Is New Again... • I am going

    to introduce some functional programming (as well as a little bit of my favorite language for it, Scala) • This is by no means a comprehensive course on functional programming • If you like what you see, I recommend a few books
  14. Everything That’s Old Is New Again... • I am going

    to introduce some functional programming (as well as a little bit of my favorite language for it, Scala) • This is by no means a comprehensive course on functional programming • If you like what you see, I recommend a few books • “Learn You A Haskell For Great Good” (Lightweight & lighthearted)
  15. Everything That’s Old Is New Again... • I am going

    to introduce some functional programming (as well as a little bit of my favorite language for it, Scala) • This is by no means a comprehensive course on functional programming • If you like what you see, I recommend a few books • “Learn You A Haskell For Great Good” (Lightweight & lighthearted) • “The Little Schemer” (if you like Lisp)
  16. Everything That’s Old Is New Again... • I am going

    to introduce some functional programming (as well as a little bit of my favorite language for it, Scala) • This is by no means a comprehensive course on functional programming • If you like what you see, I recommend a few books • “Learn You A Haskell For Great Good” (Lightweight & lighthearted) • “The Little Schemer” (if you like Lisp) • “Real World Haskell” (heavy and in depth, if you want a real dive)
  17. What is this? int multiply(int x, int y) { return

    x * y; } That’s right... it’s a function (in C/C++)
  18. What is this? int multiply(int x, int y) { return

    x * y; } That’s right... it’s a function (in C/C++) We can invoke it to calculate a value...
  19. What is this? int multiply(int x, int y) { return

    x * y; } That’s right... it’s a function (in C/C++) We can invoke it to calculate a value... int main() { int x = 512000; int y = 128; int z = multiply(x, y); std::cout << x << "*" << y << " is: " << z << std::endl; }
  20. Python shouldn’t be too different... def multiply(x, y): return x

    * y if __name__ == '__main__': x = 512000 y = 128 z = multiply(x, y) print "%i*%i is %i" % (x, y, z)
  21. Python shouldn’t be too different... def multiply(x, y): return x

    * y if __name__ == '__main__': x = 512000 y = 128 z = multiply(x, y) print "%i*%i is %i" % (x, y, z) Python has some FP influence though...
  22. Python shouldn’t be too different... def multiply(x, y): return x

    * y if __name__ == '__main__': x = 512000 y = 128 z = multiply(x, y) print "%i*%i is %i" % (x, y, z) Python has some FP influence though... print "Multiply Function: %s" % multiply # Multiply Function: <function multiply at 0x1101ef938> foobar = multiply print "%i*%i is %i" % (x, y, foobar(x, y))
  23. First Class Functions • These tricks work because Python considers

    functions to be “first class” • a function named “multiply” is simply a variable of type “function”
  24. First Class Functions • These tricks work because Python considers

    functions to be “first class” • a function named “multiply” is simply a variable of type “function” • Technically you can use pointers to do similar tricks in C/C++ but it’s far from easy (or transparent)
  25. First Class Functions • These tricks work because Python considers

    functions to be “first class” • a function named “multiply” is simply a variable of type “function” • Technically you can use pointers to do similar tricks in C/C++ but it’s far from easy (or transparent) • Many languages segregate function as if they are special, meaning they are not truly composable units of work.
  26. First Class Functions • These tricks work because Python considers

    functions to be “first class” • a function named “multiply” is simply a variable of type “function” • Technically you can use pointers to do similar tricks in C/C++ but it’s far from easy (or transparent) • Many languages segregate function as if they are special, meaning they are not truly composable units of work. • In FP, we try to leverage the function as just another tool - meaning it must be flexible, reusable and first class.
  27. Let’s Look at Scala • Python is not truly functional,

    but instead borrows some concepts of functional programming
  28. Let’s Look at Scala • Python is not truly functional,

    but instead borrows some concepts of functional programming • Instead, let’s look at Scala... which is a true blend of Object Oriented + Functional programming with a full arsenal of FP tools.
  29. Let’s Look at Scala • Python is not truly functional,

    but instead borrows some concepts of functional programming • Instead, let’s look at Scala... which is a true blend of Object Oriented + Functional programming with a full arsenal of FP tools. • Scala was created by Martin Odersky (author of Java Generics & the 1.5+ javac) at EPFL in Switzlerand
  30. Let’s Look at Scala • Python is not truly functional,

    but instead borrows some concepts of functional programming • Instead, let’s look at Scala... which is a true blend of Object Oriented + Functional programming with a full arsenal of FP tools. • Scala was created by Martin Odersky (author of Java Generics & the 1.5+ javac) at EPFL in Switzlerand • JVM based, able to interoperate cleanly with Java & existing libraries
  31. Let’s Look at Scala • Python is not truly functional,

    but instead borrows some concepts of functional programming • Instead, let’s look at Scala... which is a true blend of Object Oriented + Functional programming with a full arsenal of FP tools. • Scala was created by Martin Odersky (author of Java Generics & the 1.5+ javac) at EPFL in Switzlerand • JVM based, able to interoperate cleanly with Java & existing libraries • Used by companies such as Twitter, Foursquare, The Guardian, LinkedIn, and many more
  32. First Class Functions in Scala def multiply(x: Int, y: Int)

    = { x * y } val n = 512000 val o = 128 val p = multiply(n, o) println("%s*%s is %s".format(n, o, p)) // 512000*128 is 65536000 println("Multiply Function: %s".format(multiply _)) val foobar = multiply _ // Multiply Function: <function2> println("%s*%s is %s".format(n, o, foobar(n, o))) // 512000*128 is 65536000
  33. Going Anonymous • If a function is a truly composable

    & reusable unit, we should also be able to define it ‘anonymously’
  34. Going Anonymous • If a function is a truly composable

    & reusable unit, we should also be able to define it ‘anonymously’ • This concept is sometimes referred to as lambdas
  35. Going Anonymous • If a function is a truly composable

    & reusable unit, we should also be able to define it ‘anonymously’ • This concept is sometimes referred to as lambdas • Lambdas are one of the most borrowed FP concepts in traditional languages
  36. Going Anonymous • If a function is a truly composable

    & reusable unit, we should also be able to define it ‘anonymously’ • This concept is sometimes referred to as lambdas • Lambdas are one of the most borrowed FP concepts in traditional languages • In some cases, Lambdas exist exclusively of “first class” functions (but can be used to tackle similar needs)
  37. Anonymous Functions / Lambdas In Scala val multiply = (x:

    Int, y: Int) => { x * y } val n = 512000 val o = 128 val p = multiply(n, o) println("%s*%s is %s".format(n, o, p)) // 512000*128 is 65536000
  38. Anonymous Functions in Other Languages • Python has Lambdas as

    well (but restricts to one liners) lambda x, y: x * y # <function <lambda> at 0x10acce938> multiply = lambda x, y: x * y multiply(5, 2) # 10
  39. Anonymous Functions in Other Languages • Python has Lambdas as

    well (but restricts to one liners) • C++ 11 is even adding them to C++! lambda x, y: x * y # <function <lambda> at 0x10acce938> multiply = lambda x, y: x * y multiply(5, 2) # 10
  40. Anonymous Functions in Other Languages • Python has Lambdas as

    well (but restricts to one liners) • C++ 11 is even adding them to C++! lambda x, y: x * y # <function <lambda> at 0x10acce938> multiply = lambda x, y: x * y multiply(5, 2) # 10 [](int x, int y) { return x * y; }
  41. Tricks with Functions • Let’s look at some tricks used

    with functions when we have access to them as a proper value
  42. Tricks with Functions • Let’s look at some tricks used

    with functions when we have access to them as a proper value • Higher Order Functions
  43. Tricks with Functions • Let’s look at some tricks used

    with functions when we have access to them as a proper value • Higher Order Functions • Partial Application
  44. Tricks with Functions • Let’s look at some tricks used

    with functions when we have access to them as a proper value • Higher Order Functions • Partial Application • Multiple Argument Lists
  45. Tricks with Functions • Let’s look at some tricks used

    with functions when we have access to them as a proper value • Higher Order Functions • Partial Application • Multiple Argument Lists • Currying
  46. Higher Order Functions • Higher Order functions are one of

    the most leveraged patterns in functional programming
  47. Higher Order Functions • Higher Order functions are one of

    the most leveraged patterns in functional programming • The idea is to truly take advantage of functions as values
  48. Higher Order Functions • Higher Order functions are one of

    the most leveraged patterns in functional programming • The idea is to truly take advantage of functions as values • A Higher Order Function either ...
  49. Higher Order Functions • Higher Order functions are one of

    the most leveraged patterns in functional programming • The idea is to truly take advantage of functions as values • A Higher Order Function either ... • Accepts one or more functions as “input”
  50. Higher Order Functions • Higher Order functions are one of

    the most leveraged patterns in functional programming • The idea is to truly take advantage of functions as values • A Higher Order Function either ... • Accepts one or more functions as “input” • Produces a function as its result (“output”)
  51. Higher Order Functions • Higher Order functions are one of

    the most leveraged patterns in functional programming • The idea is to truly take advantage of functions as values • A Higher Order Function either ... • Accepts one or more functions as “input” • Produces a function as its result (“output”) • Or, technically... both!
  52. Higher Order Functions def math(x: Int, y: Int, op: (Int,

    Int) => Int) = { op(x, y) } val multiply = (x: Int, y: Int) => { x * y } val add = (x: Int, y: Int) => { x + y } val sub = (x: Int, y: Int) => { x - y } val divide = (x: Int, y: Int) => { x / y } println(math(10, 5, divide)) // 2 println(math(10, 5, multiply)) // 50 println(math(10, 5, add)) // 15 println(math(10, 5, sub)) // 5
  53. Patterns around Higher Order Functions • There are any number

    of patterns we can leverage Higher Order Functions in...
  54. Patterns around Higher Order Functions • There are any number

    of patterns we can leverage Higher Order Functions in... • Let’s talk about 2
  55. Patterns around Higher Order Functions • There are any number

    of patterns we can leverage Higher Order Functions in... • Let’s talk about 2 • “Execute Around” / “Loan” Patterns
  56. Patterns around Higher Order Functions • There are any number

    of patterns we can leverage Higher Order Functions in... • Let’s talk about 2 • “Execute Around” / “Loan” Patterns • Inline Manipulation (“map”, “flatMap”, “filter”, etc)
  57. Executing Around / Loaning a Resource val file = scala.io.Source("anon.scala")

    for (line <- file) { println(line) } // ... we forgot to close the file
  58. Executing Around / Loaning a Resource def iterateFile(file: String, op:

    Iterator[String] => Unit) = { val fh = scala.io.Source.fromFile(file) op(fh.getLines) // CLOSE IT! fh.close() } iterateFile("anon.scala", { file => for (line <- file) println(file) })
  59. Why “Execute Around”? • Enforces encapsulation and value “leakage”/”exposure” •

    User gets limited (as in you control the view of the resource) access to some resource (often IO based) • Much easier to handle cleanup, restrict access to certain values/params and “hide implementation”
  60. Manipulating Data ... Inline! val fh = scala.io.Source.fromFile("anon.scala") fh.getLines.foreach( line

    => println(line) ) // Even simpler... implicit... fh.getLines.foreach(println) // Filter out blank lines val isBlank = (x: String) => x == "" val noBlank = fh.getLines.filterNot(isBlank) val blankOnly = fh.getLines.filter(isBlank) // Manipulate on the fly, producing a new datasource val commented = fh.getLines.map{ l => "//%s".format(l) }
  61. Why Manipulate Inline? • Similar reasons to “Execute Around” come

    into play... • Enforces encapsulation and value “leakage”/”exposure”
  62. Why Manipulate Inline? • Similar reasons to “Execute Around” come

    into play... • Enforces encapsulation and value “leakage”/”exposure” • Limits access to the “core resource”
  63. Why Manipulate Inline? • Similar reasons to “Execute Around” come

    into play... • Enforces encapsulation and value “leakage”/”exposure” • Limits access to the “core resource” • More importantly, allows us to more safely control two incredibly important concepts:
  64. Why Manipulate Inline? • Similar reasons to “Execute Around” come

    into play... • Enforces encapsulation and value “leakage”/”exposure” • Limits access to the “core resource” • More importantly, allows us to more safely control two incredibly important concepts: • State
  65. Why Manipulate Inline? • Similar reasons to “Execute Around” come

    into play... • Enforces encapsulation and value “leakage”/”exposure” • Limits access to the “core resource” • More importantly, allows us to more safely control two incredibly important concepts: • State • Mutability
  66. Why Manipulate Inline? • Similar reasons to “Execute Around” come

    into play... • Enforces encapsulation and value “leakage”/”exposure” • Limits access to the “core resource” • More importantly, allows us to more safely control two incredibly important concepts: • State • Mutability • ... which we’ll elaborate on shortly.
  67. Partially Applied Functions • Taking advantage of this “reusable, composable

    value” concept ... • We can fill in some of the required arguments to a function, and then hand a “partially applied” view to someone else to finish
  68. Partially Applied Functions def multiply(x: Int, y: Int) = {

    x * y } // multiply: (x: Int, y: Int)Int /* multiply(50, 1) Int = 50 */ def squared = multiply(_: Int, 2) // squared: Int => Int /* squared(50) Int = 100 */ def cubed = multiply(_: Int, 3) /* cubed(50) Int = 150 */
  69. Accepting Multiple Arguments • Sometimes, there’s some composable beauty in

    taking multiple argument lists instead of one • Many functional languages allow this (as well as tricks around them) • Think about how we can clean up our multiply function based on the tricks we just pulled with partial application
  70. Accepting Multiple Arguments def multiply(multiplier: Int)(value: Int) = { value

    * multiplier } // multiply: (multiplier: Int)(value: Int)Int /* multiply(1)(50) Int = 50 */
  71. Currying Favor • With “Currying” we can leverage multiple argument

    lists (technically, to “curry” they must each take a single argument) partially applied • This gives us a chain of callable functions • With partial application, we can “lazily” evaluate this chain
  72. Currying Favor def squared = multiply(2) _ // squared: Int

    => Int /* squared(50) Int = 100 */ def cubed = multiply(3) _ // cubed: Int => Int /* cubed(50) Int = 150 */
  73. Higher Order Functions + Multiple Args • What I think

    of as the “beauty” of currying can also be used to clean up things like a “loan” method which takes initialization params • Separate out our function arg from our non-function args ...
  74. Higher Order Functions + Multiple Args def math(x: Int, y:

    Int)(op: (Int, Int) => Int) = { op(x, y) } val multiply = math(10, 5) { (x, y) => x * y } val add = math(10, 5) { (x, y) => x + y } val sub = math(10, 5) { (x, y) => x - y } val divide = math(10, 5) { (x, y) => x / y } println(divide) // 2 println(multiply) // 50 println(add) // 15 println(sub) // 5
  75. Higher Order Functions + Multiple Args def iterateFile(file: String)(op: Iterator[String]

    => Unit) = { val fh = scala.io.Source.fromFile(file) op(fh.getLines) // CLOSE IT! fh.close() } iterateFile("anon.scala") { file => for (line <- file) println(file) }