ted.neward@newardassociates.com | Blog: http://blogs.newardassociates.com | Github: tedneward | LinkedIn: tedneward
Answer a few questions about functional languages:
why is everybody so interested in them now?
what makes them interesting in the first place?
what does functional programming look and feel like?
See some examples of functional code
F#
Scala
Haskell
Erlang
immutable values over mutable variables
functions as first-class values
currying, partial-application of functions
expressions-not-statements
laziness/deferred execution
once bound, a binding remains constant throughout its lifetime
thus offers no opportunity for confusion/reassignment/etc
"values" are not "variables"
name/value binding is fixed once bound
A functional pseudocode example
let x = 0 // or sometimes "val" let y = 1 let z = x + y
think about common operations-if we could vary the actual operation itself as a parameter, how much work could be saved?
example: you need to iterate through a collection to...
... and each time you write it as a "for" loop, you're violating DRY
this enables the use of functions as "higher-order" functions
"take this function, and execute inside your context"
similar in some ways to a callback, but with clearer semantics
in a lot of ways, this is Inversion-of-Control all over again
Higher-order functions
let numbers = [1, 2, 3, 4, 5] let squares = numbers.map((num) -> num * num); // squares = [1, 4, 9, 16, 25]
In functional languages, we can build functions out of functions
This allows us to achieve reuse through the composition/combination of functional parts into larger functions
By doing so, we "build up" larger more complex functions
When combining several in a row using currying, this is also called "pipelining"
providing some of the parameters (but not all) to a function and capturing that as a function to be called
Partial application
let add x y = x + y let five = add 2 3 // = 5 let addBy2 = add 2 // = (anonymous fn) = 2 + y let six = addBy2 addBy2 2 // = 6
it turns out (thank you Alonzo Church!) that all functions can be reduced to functions taking one parameter and either yielding a result or another function
this permits easy "pipelining" and composition of functions in a highly reusable manner (at the micro level)
this is an outgrowth of the functions-as-first-class-citizens idea: if functions yield values, what is the practical difference between a keyword and a function?
even C++ tried to make user-defined elements look and feel like built-in constructs and vice versa
if we're really good about this, developers can create new "language" primitives and nobody will know the difference
object-oriented laziness has nothing on functional laziness
don't compute anything until absolutely necessary (but make sure to maintain the dependency graph so everything is there when needed)
laziness is highly encouraged/permissible in pure FP
just to be fair, laziness is highly desirable inside the process, not so across processes unless carefully managed
lots of things can be seen as sequences
characters in a string
fields in a record
records in a database
files in a directory
algorithmic calculations (factorial, fibonacci, ...)
lines in a file
user interactions
sequences and collections have a deep relationship
in many ways, this is the gateway to FP ideas/concepts
strongly-typed, type-inferenced
recursion
tuples, lists
pattern-matching
Strongly-typed
the dynamic language community will have you believe that it's better to write unit tests by hand than to have a system that can do common-sense checking for you
Type-inferenced
why do I have to be explicit to the language/compiler, when it can figure out what I'm trying to do and when?
Recursion
immutable values doesn't mean no state changes
instead, hold state on the stack, not in mutable memory
Tuples, lists
"bundles" of data in different directions
tuples give developers a "lightweight" object that needn't be named or otherwise formalized
Pattern-matching
switch/case is to pattern-matching as my kid's soccer team is to Arsenal or Manchester United
pattern-matching also encourages "destructuring" of data when necessary/desired
pattern-matching can operate off of collections of values
- Expressions-not-statements
// Scala object HelloWorld { def main(args : Array[String]) = { System.out.println( if ((java.lang.Math.random() * 10) % 2 == 0) "Even!" else "Odd!" ) } }
- Strongly type-inferenced
// F# let x = 1 let mult x y = x * y let swap a b = b a
% Erlang -module(tut). -export([swap/1, mult/2]). mult(X, Y) -> X * Y. swap({A, B}) -> {B, A}.
- Nested functions
// Scala def sqrt(x: Double) = { def sqrtIter(guess: Double, x: Double): Double = if (isGoodEnough(guess, x)) guess else sqrtIter(improve(guess, x), x) def improve(guess: Double, x: Double) = (guess + x / guess) / 2 def isGoodEnough(guess: Double, x: Double) = abs(square(guess) x) < 0.001 sqrtIter(1.0, x) }
- Tuples, lists * tuples are a compound data type with a fixed number of terms * lists are a singly-typed collection of elements
-- Jaskell {name="tom"; age=1} -- tuple of string * int [] -- empty list [1, 2] -- list of two integers 1 :: [2] -- 'cons'ing an integer to a list
- Recursion-over-iteration
-- Haskell fac :: Int -> Int fac n | n==0 = 1 | n > 0 = n * fac (n-1)
// Scala def sumList(xs : List[Int]) = if (xs == List()) 0 else (xs2.head + sumList(xs.tail)
- Pattern matching
// F# let rec SumList xs = match xs with | [] -> 0 | y::ys -> y + SumList ys type Expr = | Num of int | Add of Expr * Expr | Mul of Expr * Expr | Var of string let rec Evaluate (env:Map<string,int>) exp = match exp with | Num n -> n | Add (x,y) -> Evaluate env x + Evaluate env y | Mul (x,y) -> Evaluate env x * Evaluate env y | Var id -> env.[id]
# Higher-order functions * functions taking functins as parameters * functions captured as values
// F# let (|>) x f = f (x) let Square n = n * n let SumOfSquaresUpTo n = [1..n] |> List.map Square |> List.sum
# Closures * referenced values remain around as long as function does * this provides opportunities to "hide" members from the object on which a function operates, to avoid pollution
// JavaScript var myObj = function() { var value = 0; return { increment: function(inc) { value += typeof inc === 'number' ? inc : 1; }, getValue: function() { return value; } }; }();
- Partial application
// Scala def sum(f: Int => Int) : (Int, Int) => Int = { def sumF(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sumF(a + 1, b) sumF } def powerOfTwo(x: Int): Int = if (x == 0) 1 else 2 * powerOfTwo(x 1) def sumInts = sum(x => x) _ def sumSquares = sum(x => x * x) _ def sumPowersOfTwo = sum(powerOfTwo) _
Partial application vs. currying
Partial application is passing in less than the full number of arguments to a function that takes multiple arguments
Currying is a technique that is used to transform a function that takes multiple arguments in such a way that it can be called as a chain of functions (a pipeline), each with a single argument
Scala: http://www.scala-lang.org-- Programming in Scala (Venners, Odersky; Artima)
F#: http://msdn.microsoft.com/en-us/fsharp/default.aspx
Expert F# (Syme, et al; APress)
Professional F# 2.0 (Neward, et al; Wrox)
Erlang: http://www.erlang.org-- Programming Erlang (Armstrong, Pragmatic Press)
Haskell: http://haskell.org/ghc/
http://www.haskell.org/haskellwiki/Definition
Real-World Haskell (OReilly) at http://book.realworldhaskell.org/read/
Thanks to...
Simon Peyton-Jones, for the use of some of his slides from Qcon London 2008
Joe Armstrong, for the use of some samples
Martin Odersky, for the use of some samples
Don Syme, for the use of some samples
Architect, Engineering Manager/Leader, "force multiplier"
http://www.newardassociates.com
http://blogs.newardassociates.com
Sr Distinguished Engineer, Capital One
Educative (http://educative.io) Author
Performance Management for Engineering Managers
Books
Developer Relations Activity Patterns (w/Woodruff, et al; APress, forthcoming)
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010)
Effective Enterprise Java (Addison-Wesley, 2004)
SSCLI Essentials (w/Stutz, et al; OReilly, 2003)
Server-Based Java Programming (Manning, 2000)