I'm not going to teach you anything.
No sales pitches!
I.e. Haskell and Scala
I.e. Clojure and… Ruby?
I just don't do much of it so my examples are going to be biased
Sorry!
def add(a: Int, b: Int): Int = { a + b }
add(1, 1) // It returns 2! Purely!!
def fib(n: Int): Int = { if(n == 0) { 0 } else { n + fib(n - 1) } }
fib(4) // returns 10!
val add = (a: Int) => { (b: Int) => { a + b } }
add(1)(2) // Returns 3! Cool!
// List processing: val numbers = [1, 1, 1] numbers.map(i => i + 1).sum // Returns 6! Cool!
// Making decisions & causing side effects: def makeLotsOfMoney(database: DbConnection): Unit = { val monies: Array[Float] = database.getMoney() val isThereEnoughMoney = true monies.foreach { money => if(money < 1000000) { isThereEnoughMoney = false } } if(!isThereEnoughMoney && someBusinessThing() && !globalVariable) { doSomethingWithAWS() } else { globalVariable = true log.error( "Something bad happened but it's been happening for years so don't worry!" ) doSomethingElseWithAWS() } }
Functional programming is not morally better
We just think it's a great way to:
Short answer: Because we think pure functions are gods gift to the mortal world.
// Games in the datbase: ["Dwarf Fortress", "Duke Nukem Forever", "Prey", "E.T."] var gameDatabase = gameServer.getGameDatabase // Impure def filterBadGames(): Unit = { gameDatabase.remove("Duke Nukem Forever") gameDatabase.remove("E.T.") } filterBadGames() println(gameDatabase) // ["Dwarf Fortress", "Prey"]!
I started writing this example and it was so ugly I gave up.
// Games in the datbase: ["Dwarf Fortress", "Duke Nukem Forever", "Prey", "E.T."] var gameDatabase = gameServer.getGameDatabase // Impure function filterBadGames() { gameDatabase.remove("Duke Nukem Forever"); gameDatabase.remove("E.T."); } filterBadGames() console.log(gameDatabase) // ["Dwarf Fortress", "Prey"]!
// Games in the datbase: ["Dwarf Fortress", "Duke Nukem Forever", "Prey", "E.T."] val gameDatabase = gameServer.getGameDatabase // Pure def filterBadGames(games: Array[String]): Array[String] = { games .filter(_ == "Duke Nukem Forever") .filter(_ == "E.T.") } val goodGames = filterBadGames(gameDatabase) println(goodGames) // ["Dwarf Fortress", "Prey"]
-- Games in the datbase: ["Dwarf Fortress", "Duke Nukem Forever", "Prey", "E.T."] gameDatabase :: IO [String] gameDatabase = getGameDatabase -- Pure filterBadGames :: [String] -> [String] filterBadGames = (delete "Duke Nukem Forever") . (delete "E.T.") main :: IO () main = do games <- gameDatabase let goodGames = filterBadGames games putStrLn goodGames -- ["Dwarf Fortress", "prey"]
// Games in the datbase: ["Dwarf Fortress", "Duke Nukem Forever", "Prey", "E.T."] const gameDatabase = gameServer.getGameDatabase // Pure function filterBadGames(games) { games.filter(_ == "Duke Nukem Forever") .filter(_ == "E.T."); } const goodGames = filterBadGames(gameDatabase) println(goodGames) // ["Dwarf Fortress", "Prey"]
Super easy to test
"filterBadGames" should { "keep Prey" in { filterBadGames(["Prey"]) must beEqualTo(["Prey"]) } "filter out Duke Nukem Forever" in { filterBadGames(["Duke Nukem Forever"]) must beEmpty } }
Bam!
Everything you need to know is in one place.
def filterBadGames(games: Array[String]): Array[String] = { games .filter(_ == "Duke Nukem Forever") .filter(_ == "E.T.") }
I know for a fact that filterBadGames only uses an array of strings. Nothing else!
I also know it's not going to do something unexpected!
The static typing is actually telling the truth!
def filterBadGames(games: Array[String]): Array[String]
This function is making a promise: Give me some strings and I will give you back some strings. Nothing more, nothing less
Pure functions are good friends: They keep their promises
Pure functions can't do anything!
Just kidding
A solution that leverages pure functions
Makes it possible to write pure functions
Makes it easier to apply functions to different types of containers
def add5(input: Int): Int = input + 5
// add5 works on ints! add5(10) // Returns 15
// Functor lets us use it on lists! List(1,2,3).map(add5) // returns List(6,7,8)
// And Options Some(20).map(add5) // Returns List(25)
// And Future! Future(1).map(add5) // Returns Future(6)
And lots of other stuff!
Combine functions in parallel
Sequence functions together
This is used for IO in FP as IO is sequential!
But Monads are not the core of FP and you can do FP without monads.
Makes it easier to apply functions to recursive data
Makes it easier to apply functions to nested data structures
Make it easier to pick which function to apply to some data
Questions?