RubyConf Mini 2022

Functional programming for fun and profit!!

Functional programming for fun and profit!!

by Jenny Shih

In her talk at RubyConf 2022, Jenny Shih explores the benefits of functional programming (FP) within Ruby projects, emphasizing that FP is not exclusively beneficial for languages traditionally associated with it, like Elixir. She clarifies misconceptions about FP while advocating for its significant advantages in writing cleaner, more reliable Ruby code. The main points of her presentation include:

  • Functional Programming Basics: Shih defines functional programming as a distinct programming paradigm focused on functions as the primary building blocks, differing from object-oriented programming (OOP) which encapsulates state and behavior within objects.

  • Immutability: A crucial concept in FP, immutability means that once a value is set, it cannot be changed. Shih stresses that immutability eliminates complexities associated with shared state, which can lead to unexpected bugs. She suggests tools such as the 'dry-struct' gem and Ruby 3.2’s data definitions for achieving immutability in Ruby.

  • Pure Functions: Shih explains that pure functions produce outputs strictly based on their inputs without causing side effects. This design principle minimizes complexities and enhances predictability in coding, helping developers avoid common pitfalls related to state mutations.

  • Time Management and Concurrency: The speaker discusses how functional programming tackles concurrency with greater efficacy than OOP. She shares an example from Elixir involving the actor model for concurrency, contrasting it with Ruby’s Global Interpreter Lock (GIL). Shih highlights Ruby 3.0's introduction of 'Ractor' as a step towards improving concurrency without shared state, enhancing efficiency further.

Jenny's conclusion emphasizes that the integration of immutable data and pure functions leads to more reliable and maintainable code, encouraging Ruby programmers to adopt FP principles. She acknowledges that embracing a new paradigm requires practice and adaptation, providing resources for further exploration into functional programming. Overall, her message is one of empowerment for Ruby developers, demonstrating that they can benefit from FP without needing a complete mastery of its concepts right away.

00:00:00 Hello, everyone! Welcome to my talk.
00:00:02 Today, I will be discussing functional programming (FP) for fun and profit.
00:00:07 The title might be misleading because, as I rehearsed this morning, I realized it's not as fun as I thought it would be.
00:00:18 But since you’re already here, you might as well stay till the end!
00:00:27 I want to open up with a GitHub article titled "Functional Programming is Finally Going Mainstream." This article discusses the increasing adoption of functional programming over the years.
00:00:48 You might have seen discussions, tutorials, or explanations about functional programming concepts here and there, as well as memes and parodies related to some of its concepts.
00:01:07 You might be wondering what functional programming really is and whether you should care about it if you just want to write your Ruby code.
00:01:18 After all, Ruby is known for being object-oriented, where everything is an object.
00:01:36 Today, I want to convince you that functional programming can help us write better Ruby code.
00:01:41 Before we dive into that, let me share a bit about myself. My name is Jenny—it's easier for everyone to call me that—and two big events happened in my life two months ago: I started working at Shopify and moved from Taiwan to Toronto, Canada.
00:02:05 I primarily program with Ruby, but there was a time when I almost transitioned to Elixir, which is one of the main reasons I am giving this talk.
00:02:18 You’ll soon find out that I drew a lot of inspiration from Elixir for this presentation.
00:02:31 Also, if you visit my personal website, you will see a string of chicks that follow the cursor wherever it goes, which is kind of therapeutic.
00:02:43 Okay, with that introduction out of the way, let's talk about the functional programming paradigms.
00:02:56 According to Wikipedia, functional programming is a way to classify programming languages based on their features.
00:03:06 There are many programming paradigms out there, such as object-oriented programming, characterized by message-passing between objects and encapsulating state and behavior.
00:03:16 Functional programming, which is the focus of today's discussion, has its own unique set of features, which I will talk about shortly.
00:03:27 To help us understand these concepts better, let’s first look at a mental model.
00:03:39 Consider three elements: value, behavior, and time.
00:03:47 To illustrate this, let’s look at an example: a function 'puts' that prints out the string "Hello, World!" and returns nil.
00:03:54 In this case, we see two components: behavior—what the function does, and value—what it holds.
00:04:01 In another example, if we assign 'rubyconf mini' to a variable 'curve' and later change it by uppercasing it, we are illustrating the element of time.
00:04:14 Between lines of code, the bytes in the machine change, resulting in the value of the variable 'curve' being updated.
00:04:27 To understand what time represents, we can think of it as the value of an identity we hold on to during a specific period.
00:04:42 This is my mental model of what object-oriented programming looks like: an object encapsulates value and behavior, while time is implicitly involved because values can change over time.
00:05:00 In functional programming, each element has a distinct concept associated with it, and the goal today is to explore each of these elements.
00:05:17 Let's begin with the first element: value.
00:05:27 When discussing value in functional programming, we need to address immutability.
00:05:40 Immutability means that once you initialize a value, you do not change it.
00:05:58 In Ruby, this is achieved by freezing objects, such as an array.
00:06:06 When defining objects using the class keyword, you should avoid using attribute writers and accessors, which allow other objects to mutate its attributes.
00:06:20 This goes against the definition of immutability, and thus, we must understand why immutability is important.
00:06:36 With immutable objects, we eliminate shared state complexities. We don’t need to care about when and where objects change because they never do.
00:06:48 Consider this example: I have a flight scheduled for November 14th, and if my friend copies my flight and changes the date to November 13th, it reflects on my flight as well.
00:07:03 This occurs due to shared state: by mutating state, we can run into unexpected bugs.
00:07:24 In programming, immutability helps us avoid these pitfalls.
00:07:34 Now, how can we implement immutability in Ruby? The first piece of advice is to think twice about every state limitation you introduce.
00:07:50 Ruby, at a language level, doesn’t support immutability natively like some other programming languages, so it's up to us to avoid writing mutable code.
00:08:02 This good habit will reduce the likelihood of producing bugs.
00:08:15 For instance, there’s a gem called 'dry-struct' that is part of the dry-ruby collection.
00:08:25 This gem offers powerful and useful tools supporting functional programming ideas, including immutable data structures.
00:08:40 In Ruby 3.2, there’s also a new feature called data defined, which provides simple immutable value objects. You can play with this in the latest version and find excellent documentation.
00:09:04 So, although we have tools to aid us, it’s equally important to be mindful of state mutations and their consequences.
00:09:20 We have now covered the value aspect of functional programming. By enforcing immutability as a constraint, we create programs that are easier to debug and reason about.
00:09:38 Next, let's examine behavior in functional programming.
00:09:54 The core concept is the use of pure functions.
00:10:05 A pure function meets two conditions: it depends only on its input arguments and does not depend on any external values.
00:10:15 For example, if we create a function that uses the Time class, it’s not pure because it depends on the module, resulting in different outputs even with the same input.
00:10:36 In contrast, if we pass the timestamp itself as an input, we create a pure function, as its output can now be predicted based on the input.
00:10:50 The second condition for a pure function is not mutating the state, which implies having no side effects.
00:11:02 If a function alters the string value passed to it, it cannot be considered pure, as doing so mutates its state.
00:11:20 To make this function pure, we must create a new string without altering the original.
00:11:35 Why do we want to restrict ourselves in this way? Joe Armstrong, the creator of Erlang, points out that all languages come with an implicit environment.
00:11:50 This implicitness can lead to unexpected consequences and confusion.
00:12:09 Avoiding implicit environments allows us to focus on smaller, manageable parts of a problem.
00:12:16 This approach leads to improved efficiency and bug prevention, as we can concentrate on details.
00:12:29 To summarize, by striving for pure functions, we minimize the complexities that arise in programming.
00:12:45 The next element we’ll discuss is time, particularly in relation to processes and concurrency.
00:13:00 In object-oriented programming, concurrency can be challenging due to time being an implicit factor.
00:13:12 This leads to potential issues, such as race conditions and deadlocks when multiple processes run simultaneously.
00:13:28 The infamous Global Interpreter Lock (GIL) in Ruby restricts it such that only one thread may execute at any time, improving safety but reducing concurrent capabilities.
00:13:47 On the other hand, functional programming tackles this issue by explicitly treating time as a separate element, which can simplify concurrency.
00:14:05 In an example from Elixir, a functional programming language, concurrency is inherently implemented through its actor model.
00:14:25 In this model, each process acts as an individual unit that can only communicate with others through message passing, ensuring no shared state.
00:14:39 Elixir processes are lightweight, allowing millions to run simultaneously, thereby enhancing concurrency.
00:14:54 Ruby has introduced a similar concept called 'Ractor' starting from version 3.0, which aims to improve concurrency.
00:15:07 In a Ractor model, every Ractor can run in parallel without sharing state, making computation safer and more efficient.
00:15:20 If we attempt to send objects between Ractors, they must be immutable to maintain the simplicity of state.
00:15:32 I’m looking forward to future developments in Ractor and the potential it offers for Ruby programming.
00:15:45 In conclusion, functional programming promotes the use of immutable data and pure functions.
00:16:02 Together, these concepts not only enhance code reliability and maintainability but also provide an excellent foundation for concurrent programming.
00:16:17 The best part is, you can apply these principles in Ruby!
00:16:29 I hope you found value in this talk and encourage you to explore further into functional programming.
00:16:45 Remember, adopting a new paradigm requires practice and adaptation.
00:17:02 These are the resources that I found useful, and I will upload my slides later.
00:17:10 Thank you for your time!