Julian Doherty
Functional Programming For The Anxious Developer

http://www.rubyconf.org.au

Programming involves dealing with an overwhelming amount of complexity. The human brain can only deal with so much information to process before anxiety kicks in and your ability to proceed suffers. Functional programming provides tools to manage the combinatorial explosion of state and logic. Here we'll cover some practical uses of functional programming techniques in Ruby that can be directly applied to your everyday work.

RubyConf AU 2017

00:00:08.160 Okay, thank you all. I want to tell you a story. This is a story about a plucky young developer excited about his first project.
00:00:15.599 He's skipping through the fields, and it was lovely. There were lots of squirrels around, and they were merry. Anything was possible, limited only by his own imagination.
00:00:28.880 So he set to work producing mountains of code, having so much fun cranking it out. It was awesome, and then, hang on, what the hell? It's not supposed to happen. Uh, no, myth error? Huh, okay, let’s fix that.
00:00:42.600 He was skipping, and the squirrels were merry. The project launched, and everyone thought it was pretty good. He went on to more projects, more code, and traveled the world, working on bigger things and with bigger teams.
00:01:05.000 Then, crap, that should be okay. What's wrong there? Sorry—ah, yeah, double equals. It's embarrassing; two minutes of my life I'm never getting back.
00:01:18.960 Before long, he was in charge of his own projects with his own team. Things were getting more interesting, with lots of data and lots of code, providing many chances to do wonderful things.
00:01:32.200 With the help of his new and amazing colleagues, they got it done. But then it happened again. Okay, what the hell? I already fixed this. I don’t know what's going on. No myth error? Huh, concurrency? Race conditions? It’s like a one in a million chance that’ll ever happen again.
00:01:43.119 I’ll just keep running it. Code runs like a billion times a day, but it won’t happen again. So, things are getting tougher for our plucky young developer. He’s not so young and experienced anymore and has started to believe himself a little bit invincible.
00:02:04.320 Now, he has been around the block a few times and figured out what’s going on, but the enormity of the task is starting to sink in. The enormity of the tasks in programming gets worse.
00:02:22.879 Don’t do this with ActiveModel or ActiveRecord; do it differently—it’s better. The hours got longer, the demands got higher, and he was juggling multiple clients and projects, all while keeping an eye on the team.
00:02:41.879 He was reviewing code and still needed to be hands-on because of the dreaded lurking fear of becoming post-technical. He learned not to drop tables on students, but small things began to cause anxiety.
00:02:54.560 He wasted ten years of his life due to time zone issues. Things got much harder and took longer, leading to anxiety about small things. Bugs that once seemed trivial were now hard to resolve.
00:03:20.920 The 80-hour weeks soon became the norm, and the code didn’t come out any faster. Anxiety worsened until it wasn't just a feeling anymore; it became this constant background hum.
00:03:33.599 Progress got slow. The young developer got older, more cynical, and felt pretty burnt out. Over time, he experienced constant anxiety and panic attacks, making that the new normal.
00:03:53.439 It took months to recover, taking it slow to feel somewhat human again. Lots of talking helped, but the anxiety was still there. It sneaks in everywhere.
00:04:08.200 If this sounds familiar, there are some things you can do about it. First, Google it. You’ll find a lot of resources. Seriously, check the first page about anxiety—it’s good information.
00:04:25.600 Stuff like Beyond Blue and the behavioral health initiatives mentioned before, they sound awesome. If you don’t think you need help, you probably do. Talk to someone, especially a GP; they’re really good.
00:04:45.400 Exercise, meditate, see friends and family—just take care of yourself. Anyway, moving on, this is kind of how new developers see the world.
00:05:00.240 It’s like a green, awesome, infinite field of possibilities, kind of like the Windows XP desktop wallpaper. It’s unlimited—you can do anything as soon as you figure out this coding thing.
00:05:24.560 But it can be dangerous as hell, and new developers don’t even know it. This is how experienced developers see the world: as a hostile war zone.
00:05:43.680 There are all kinds of dangers lurking around—landmines, barbed wire, machine guns—and you build up scar tissue from painful mistakes.
00:06:02.080 You learn to be careful through lots of experience, often becoming a little paranoid. Both of these worlds are hard to work in, and neither is very productive.
00:06:13.720 What we really want to get to is something more like a working farm field. It’s not a dreamscape of youthful boundless fun. You’re not going to be skipping around merrily, but it’s not a nightmare battlefield either.
00:06:30.199 You don’t have to worry about landmines; it’s a productive, real place. The difference between those two worlds is fear.
00:06:47.760 Let’s go back a few hundred thousand years: you’re a prehistoric human walking past a watering hole and you see a lion hiding in the bushes.
00:07:06.480 All animals experience fear, and you get a rush of adrenaline. Your breathing and heart rate increase, and you get clarity and focus, enhancing your physical performance.
00:07:23.680 This response triggers your fight or flight reflex. It biases you towards action, which helps you survive.
00:07:31.400 But there’s another side to that. Since humans are cursed with higher-level thought, we walk past that watering hole and remember the lion we saw last week.
00:07:50.160 Our imagination kicks in: what if there’s a lion hiding there again? We anticipate the future, planning, but the fight or flight reflex is triggered again even with no lion there.
00:08:06.800 This instinct is sensible and good for survival, but it creates a horrible long-term feeling, especially with ongoing anxiety. It gives you a bias toward preventative behavior rather than action.
00:08:18.160 Now, in the modern world, if you've ever been in a production outage, that’s kind of what it’s like.
00:08:25.080 It’s a strong fear response, a sense of clarity, focus, and a bias towards action. Humans generally excel in that environment.
00:08:38.560 Five people who know what they’re doing in the room during a crisis can get things done quickly. But anxiety is also a very modern phenomenon.
00:08:54.799 The fight or flight reflex gets triggered, but there’s nothing to fight; there are just things that might go wrong, and the complexity of modern programming means there's a myriad of potential issues.
00:09:10.720 This can lead to analysis paralysis; humans are not well suited to this kind of functionality, and that’s where the problems begin.
00:09:25.760 What this really impacts is working memory, the space in cognition necessary to work on a task. The greater your working memory, the more complicated tasks you can manage.
00:09:41.720 You may have heard of the theory of seven plus or minus two: it suggests how many chunks of information you can hold in memory at once.
00:09:55.000 When you’re under stress, your working memory reduces, and chronic anxiety can lead to a longer-term decrease.
00:10:01.720 Thinking about all the danger zones in your code can have the same effect, crowding your working memory with concerns unrelated to the task at hand.
00:10:18.920 The core thing you’re trying to do gets crowded out by all these other distractions. The best way to optimize your working memory is to stop needing to put things in there.
00:10:29.960 Let’s go forward a few hundred thousand years to ancient Sumeria, what is now Iran and Iraq, about 6,000 years ago. Lions aren’t a threat to people anymore, but they keep eating our sheep.
00:10:46.000 So, we want to keep track of how many sheep we lose, and we use clay tokens for that. They understood this mechanical system; one token represented five sheep, another three, and we combined them.
00:10:56.400 It was a very mechanical process; you’d have these stones on the ground and you could mess it up—if a sheep walked through your stones, you’d lose track.
00:11:03.760 Recounting would mean putting them all back where they were, but it didn’t work very well. Moving forward a bit to ancient Babylonia, three and a half thousand years ago, written numbers were invented.
00:11:10.760 Arithmetic had been invented, which removed much of the physical work. This became an abstract and easier-to-reason-about system—immutable and stateless.
00:11:26.400 It was an expression of facts rather than physical tokens. You can repeat this work from an N state, going forward and expanding on it, and modern numbers in math are an extension of that system.
00:11:36.000 You might think of this: we have 27 sheep. If we lose 3, how many do we have left? We have 24. This is easy to reason about because it is completely immutable.
00:11:57.460 You can’t have a sheep walk through the count and accidentally change it; pure, stateless, repeatable calculations occur with no mechanical moving parts to mess things up.
00:12:08.160 That's not imperative programming and errors are straightforward to debug. Functional programming reduces anxiety by requiring less cognitive load. You might break things, but identifying where you messed up becomes easier.
00:12:27.280 Ruby does have some tools for functional programming, acting somewhat as a gateway to it. Ruby was my functional programming gateway drug when I learned it and Erlang around the same time.
00:12:41.200 Reading about functional transformations in Erlang while learning about map, inject, and other functional concepts in Ruby exposed me to these principles.
00:12:58.160 You need to take advantage of Ruby’s capabilities because there are useful bits within it. It won’t be pure functional programming as you may encounter in Haskell, for example.
00:13:06.720 You can try functional programming; it’s not complicated. It’s simply working with values—numbers, strings, objects, and data. Functions are things that take in arguments.
00:13:22.116 You have things that pass in arguments and do something with them, passing values out thereafter—easier to reason about, with fewer moving parts involved.
00:13:38.399 Pure functions are key: same inputs should always yield the same output. They do not exhibit non-deterministic behavior, which means functions won’t change external state during execution.
00:13:54.239 They don’t mutate their arguments; if you do that, we can’t be friends! Pure functions are great because they help reduce clutter in your working memory.
00:14:09.760 When I see a pure function, I focus on what's in front of me—local variables and passed arguments—local scope keeps everything cleaner.
00:14:27.599 One key part of this discussion is immutability—you don’t change objects after you create them. Instead, create a new object and copy the state over.
00:14:38.799 Using persistent data structures can help here, and we’ll talk more about that shortly. Here’s an example: we have a simple class keeping a flock of sheep.
00:14:55.640 We manage flock size and create a new instance of a flock whenever sheep are eaten, which allows us to track history and maintain state.
00:15:13.080 Algebric is a library that lets you create types in a weird but useful way in Ruby. The cool thing is that these don’t permit you to define methods on the types you create without expanding the module.
00:15:29.760 So, we created a flock class with an eat method that gives you a new flock with a different size but keeps the original unchanged.
00:15:45.040 Hamster is another great library for creating immutable collections. It lets you handle things like hashes, vectors, lists, and sets efficiently.
00:16:01.600 It allows you to create a new instance of a data structure while keeping a reference to the original. Immutable collections ensure that you don’t have to worry about things changing underneath you.
00:16:17.440 Honestly, there’s a lot of good stuff from libraries around immutability, as Tim mentioned earlier today. You don’t want your code changing variables or having another thread alter variables unexpectedly.
00:16:34.640 You can rely on the fact that your variable will never change, allowing for easier reasoning.
00:16:48.480 Statelessness is another critical concept. While immutability is cool, having statelessness helps a great deal when coding.
00:17:06.240 This is the big difference between traditional object-oriented Ruby and functional languages. It’s often about moving data rather than changing it.
00:17:21.120 A flock class where a sheep walks through and disturbs the flock size creates uncertainty, thus learning encapsulation helps with this.
00:17:38.360 Sometimes, you may not even need state at all; you can have a function that says: here's some input, we do a transformation, and here's a new value that comes out.
00:17:54.080 You can chain these functions together, avoiding the need for stored state.
00:18:07.760 If you think of a world with no variables—just functional expressions and transformations—everything becomes much easier to reason about.
00:18:20.600 If you remember one thing today, it's that you can achieve 90% of the benefits of functional programming by practicing pure functions, immutability, and statelessness.
00:18:37.440 Once you grasp these concepts, you’re already well on your way. There’s also cool stuff like monads, but they’re less critical than those basics.
00:18:51.480 If you are coding in Ruby, Enumerable is your best friend. The Array class inside Ruby is implemented in terms of Enumerable.
00:19:06.560 All you have to do is implement the each method, which yields from its internal data structure. To implement by hand is not practical; you would usually delegate to an array.
00:19:20.840 Using Enumerable grants you map and inject for free—vital tools for almost any transformation. These two functions are the bread and butter of functional programming.
00:19:37.040 Map is a higher-order function—one that takes in another function—and lets you create an immutable functional transformation.
00:19:56.200 Inject allows you to take a large data structure, like an array, and reduce it to a single value. For example, you take an array of numbers and use an accumulator to add them together.
00:20:14.640 Starting with zero, add one to it, and you get one. Add two to that, and you get three, then five, then ten. All of this is immutable and stateless, driven by pure functions.
00:20:33.040 When using each in functional contexts, you must remember that it operates mutably. Using each inherently requires changing state.
00:20:46.360 While performance in Ruby may not always shine, you can still benchmark to find if it meets your needs. Hamster is a great option to improve efficiency with functional elements.
00:21:05.040 Using imperative code is acceptable when necessary. Want to push something through a system? Choose the best approach.
00:21:21.600 When looking at functional data structures like algebraic types, I would love to delve deeper, but I can’t due to time constraints.
00:21:33.560 Monoids are an excellent concept for us to explore while understanding the functional programming paradigm.
00:21:45.760 A monoid's definition consists of a binary operator that combines two things, while possessing an identity that doesn’t alter the result.
00:22:07.560 It follows associative behaviors so if you add 1 plus 2 to equal 3, it works out the same as adding 3 to 1 plus 2. This feature makes it easy to apply across multi-threaded environments.
00:22:24.560 Monoids appear in various structures such as arrays and hashes. When merging, they perform the same way as integers by adding together.
00:22:37.600 Here’s a big flock example with a shepherd utilizing algebraic constructs. The flock size starts as an empty entity, building from there.
00:22:51.680 Why does this matter? When you add stuff together, it becomes immutable, pure, and stateless. This allows melding them together into something new.
00:23:05.960 Let's quickly construct an example of a shopping cart in this immutable way, one where customers can add items without altering previous data.
00:23:24.000 We manage a shopping cart, which records added items while avoiding mutations. If a user adds items while logged out, we need to merge both carts when they log back in.
00:23:38.160 Using monoids, we create an empty shopping cart instance to begin with. Each cart possesses a creation date, but you can take the minimum date offered when merging.
00:23:54.000 If there’s an action regarding a count, our empty instance values keep merging without losing original context—all managed functionally.
00:24:08.960 This approach circumvents extensive complex logic; we utilize pure functions to append any number of carts back together!
00:24:24.480 For integers, merging is intuitive. Don’t monkey patch to keep your code clean; it risks causing chaos across numerous implementations.
00:24:39.440 If you change data structures based on integer changes, change them around the right ways. Creating abstractions aids massively.
00:24:53.680 Let’s discuss how to combine dates based on specific business logic. When comparing creation dates, you may favor the earliest.
00:25:10.560 When combining these dates, ensure your logic is sound and effortless. Have a minimum threshold to gather all entries, merging flawlessly.
00:25:27.680 As users input items into their carts—signed in or not—we can sort items that flow with the actions without state loss.
00:25:44.480 Simple implementations yield efficient processes without deep convoluted logic. Achieving results seamlessly allows for easier functionality.
00:25:59.000 Thus, you can append each user's cart to a signed-in cart with minimal effort! It’s about the concept of isolation.
00:26:15.360 Finally, the goal is to find common isolated patterns and simplify your approach. You should strive for laws without any special edge cases.
00:26:30.080 For instance, as Isaac Newton illustrated the laws of motion that aptly summarize reality, you should identify and define simple, rational patterns in your programming.
00:26:46.080 Make use of the simplicity of thoughts guiding your logic. All of this combined can significantly reduce the anxiety pressure and cognitive misunderstandings.
00:27:00.360 During complexity, we acknowledge that variables and structures can cause congestion in your working memory. Let’s hope to define structures that guard against new layers of confusion.
00:27:16.080 Thank you for listening.
00:27:47.600 If you have questions for Julian, please utilize the event app to share your thoughts on his talk.
00:28:02.240 We’ve had some interesting existential exchanges on if a variable that never changes is still a variable. It’s probably more of a value.
00:28:10.160 Lots of comments have praised your talk, so that’s nice. If anyone has any questions, please go ahead!
00:28:25.480 You did a wonderful job, while I didn’t grasp everything. Feel free to ask about your production experience with Elixir and Go!
00:28:40.920 I honestly don’t have extensive Elixir production experience, even though I advocated for its use at Inato—that was quite the journey!
00:29:01.200 Despite leaving, they all transitioned to using it instead, which marked a pretty significant change!
00:29:16.640 For context, it’s worth noting Elixir is designed for reliability—like our old marketing spiel suggests—comparably low maintenance.
00:29:31.120 It’s well built to handle production loads with better performance than Ruby due to the Erlang VM.
00:29:40.720 Regarding your concern about naming when applying functions, things can get complex.
00:29:54.799 If you find yourself struggling with long variable names due to transformations, consider revisiting functional programming patterns that help keep names easy to operate.
00:30:09.760 Immutability stands out as a big part of functional programming. Treat your functions similarly to ensure presets stay intact.
00:30:25.720 The techniques I’ve discussed today stem from a development process focusing on desired end results. I urge you to challenge embedded mindsets.
00:30:39.960 The fundamental role of immutability results in clean attributes across functions. If you’re looking to apply these ideas directly, Elixir beckons!
00:30:51.240 It's extremely accessible if you are familiar with Ruby's syntax, while still offering a distinct programming approach.
00:31:05.480 Overall, we’ve covered significant ground today. Thank you so much for your engagement!