## FP is the new OOP * Michael Snoyman * Functional Conf 2022 (Virtual!) * March 25, 2022 --- ## I am not a functional programmer * I've worked with Haskell for 12+ years professionally * I use functionally-inspired code in other languages I use (Rust, TypeScript, even older Java) * I don't consider myself a functional programmer * Primary reason: I don't know what a functional programming language is! --- ## Let's talk psychology * Credit to my good friend, Adam McCullough * Freud pioneered much of the field of psychology * These days, when you say "Freud" people think of Oedipus complex, Freudian slip, etc. * That's because... --- > The stuff he got right are, now, transparent to people -- because they've become ubiquitous to our understanding of psych. --- ## What is functional programming? * I've been asking this question in talks for about 10 years! * Higher order functions * Immutability * Declarative coding * Lazy generators * Rich type systems * Type classes Fair list? Maybe. --- ## Is C a good language? * No, it's terrible!!! * Pointers galore * lol no generics * Manual memory management * Weak abstraction capabilities Sounds fair, right? Wrong! --- ## Better question: why was C invented? It's better than assembly! * Portability * Control structures * Type safety * Code reuse * Easier memory management Today, those points look like weaknesses of C. However, C was a huge step forward --- ## Object-oriented code sucks This is an FP conference, so QED * Big blob of global mutable state * Class hierarchies make no sense * `AbstractClassBeanFactoryFactory` * Bad abstractions --- ## There's a lot more to OOP * Combine data and code * Name overloading * Encapsulation * Ideally _good_ abstractions * Generics * Programming to interfaces Critics of OOP don't point to these concepts (usually). Why? --- ## Because those concepts won ```haskell sum :: (Num a, Foldable t) => t a -> a sum = getSum . foldMap' Sum ``` * This is great, idiomatic Haskell code * We're programming to interfaces (`Num`, `Foldable`) * We're dealing with abstractions * We've encapsulated the complexity of `Sum` wrapping/unwrapping * OK, that's a bit of a stretch... * Name overloading dominates, fully generic code Good Haskell code is following OOP principles! --- ## Put down the pitchforks * I'm being incredibly fast and loose with definitions * People will object to my definitions of FP, OOP, etc. * My points here is _not_ to argue for some specific definitions * My real question is: why do so many of us despise OOP, while seemingly adhering to its principles to some level? --- ## Reviewing the history * C made significant improvements on the programming status quo * Most of those improvements were adopted by future languages * C is now known for the "crappy" parts left behind * OOP: rinse and repeat * What's happening with FP? --- ## My favorite "FP" features * Strong types, ADTs * Cheap newtype wrappers * Pattern matching * Type classes Rust and TypeScript both have these (to some extent) Neither of those is an "FP" language --- ## Imperative languages are evil! Look at how bad this C code is! ```c #include <stdio.h> int main() { int total = 0; for (int i = 1; i <= 10; ++i) { if (i % 2 != 0) continue; total += i * 3; } printf("total == %d\n", total); return 0; } ``` * Mutable state * Unnecessary short-circuiting * Convoluted logic --- ## Beautiful FP in Haskell ```haskell main = putStrLn $ "total == " ++ show total where total = sum $ map (* 3) $ filter even [1..10] ``` * Clean separation of logical components * Fully immutable code * Sensible combinators like `sum` and `map` * Lazy generation of values I've used variations on this code many times in the past --- ## Idiomatic Rust ```rust fn main() { let total: u32 = (1..=10).filter(|x| x % 2 == 0).map(|x| x * 3).sum(); println!("total == {}", total); } ``` * Functionally identical to the Haskell code * Provides the same advantages * For now, Rustaceans will call this "functional style" * But quickly this is simply becoming "idiomatic Rust" --- ## Crappy Rust code You can still write bad code in Rust ```rust fn main() { let mut total = 0; let mut i = 0; while i <= 10 { i += 1; if i % 2 != 0 { continue; } total += i * 3; } println!("total == {}", total); } ``` * But people won't tell you "that's OOP" or "that's imperative" * They'll tell you "that's not idiomatic" --- ## Crappy Haskell code Just like you can write non-functional code in Haskell if you try hard enough ```haskell import Data.IORef main = do totalRef <- newIORef 0 iRef <- newIORef 1 let loop = do i <- readIORef iRef if i > 10 then pure () else do writeIORef iRef $ i + 1 if even i then do total <- readIORef totalRef writeIORef totalRef $ total + i * 3 else pure () loop loop total <- readIORef totalRef putStrLn $ "total == " ++ show total ``` --- ## What's my point? * Functional programming has introduced amazing new concepts to the programming world * We're all better off for FP's influence * The best parts have _already_ been stolen by other languages * Lambdas have been introduced all over the place * Map/reduce is now an ubiquitous concept * Or my fun JavaScript/`Result` story... --- ## JavaScript and `Result` * Team of JavaScript developers needed some Rust/WASM help * Didn't know we were a Haskell company * Needed help understanding the `Result` type in Rust * Started explaining how it worked... * "Oh, you mean it's a Monad?" --- ## The next generation * New developers will learn languages with FP concepts built in * JavaScript's `forEach`, immutable-by-default, pattern matching... all "normal" programming * FP-focused languages will no longer stand out from the pack for these features * Instead, like imperative and OOP before it, FP languages will stand out for what other languages _haven't_ adopted --- ## The cheese stands alone What FP features are being ignored? * Laziness (besides lazy generators) * Purity (to some extent) * Higher Kinded Types (to my chagrin) * Category Theoretic nomenclature (though Monad surprisingly has caught on) --- ## What do we do about it? * They've stolen all our great ideas! * FP can no longer compete * What do we do? --- ## Complain about it * "Haskell did that first" * "That's better in ML" * "That's not as elegant as in Scala" Outcome: ostracize developers. Don't do this. --- ## Admit defeat * Oh well, FP languages are a waste of time * Let's jump on the _other language_ bandwagon It's missing the big picture! --- ## Declare victory * FP concepts have won the day! * This is what we actually cared about all along * FP languages can quietly die while better languages take over Much closer, but still not fully satisfying. --- ## Declare our niche * Haskell's STM is IMO unrivaled in any other language * Ability to write complex concurrent code with the `async` package is amazing * Parser combinator libraries are still revolutionary * Embedded DSLs via arbitrary operators still best-in-class FP languages still excel beyond the features adopted by others, play to that advantage! --- ## Barriers to entry are reduced * When I started learning and later teaching Haskell, massive barrier to entry * The learning curve is no longer as high as it was * Functional concepts are familiar * Great sell to "normal" programmers: learning Haskell will help you write better FP code in Java/JavaScript/etc. --- ## Learn from the market * FP has been the petri dish for new ideas * Some of those ideas have flourished * Others have been ignored * We shouldn't dogmatically stick to our guns * Maybe it's time to admit that some of our ideas aren't keepers Which leads to my final point... --- ## Humility * FP languages stand on the shoulders of giants * It's worth going back and seeing what ideas _we_ should be stealing * It's OK to adopt established best practices from industry * Yes, FP improves on many *local maxima* situations in its predecessors * But sometimes those ideas are just good, and we've rejected them --- # Questions? Thanks all!