Talks

The Functional Alternative

The Functional Alternative

by Ju Liu

In the session titled "The Functional Alternative" presented by Ju Liu at RubyDay 2023, the discussion centers on the differences between imperative and functional programming using a live coding exercise in Ruby. The presentation began with a straightforward task of generating random numbers and constructing a histogram from them, which served as the basis for illustrating the challenges associated with imperative programming. Liu guided the audience through the process, encouraging interaction and collaboration.

Key Points Discussed:

- Imperative Programming Exercise:

- Liu started by generating 100 random numbers and saving them in a file.

- He explained how to create a histogram that visually represented these numbers using hashes, showcasing the coding logic in Ruby.

  • Complexity in State Management:

    • The exercise became more complex when attempting to group consecutive numbers and succinctly summarize them.
    • Liu noted the need to monitor state (the previous number) and introduced variables to manage this state for the grouping task.
    • This highlighted the cognitive complexity that arises from mutable state and the difficulties in maintaining clarity in imperative code.
  • Introduction to Functional Programming:

    • Liu contrasted the struggles faced in imperative coding with the principles of functional programming, which favors immutability and avoids state changes.
    • He emphasized the importance of separating data transformation from output logic to enhance code clarity and reduce cognitive load.
  • Data Transformation Approach:

    • A functional programming strategy was proposed where data is transformed independently from printing logic, making the code cleaner and easier to maintain.
    • Liu introduced the concept of using tuples to represent groups of numbers and indices clearly, thus improving the management of transformation operations.

Conclusions and Takeaways:

- Transitioning to functional programming can lead to cleaner, more understandable code and reduce the potential for bugs associated with mutable state.

- Programming paradigms can greatly influence code clarity and the ease of future modifications.

- By implementing functional programming concepts, developers can achieve robust and flexible solutions that facilitate healthier coding practices.

- The talk concluded with a Q&A session, inviting further discussion on integrating these programming philosophies into everyday coding routines, fostering continued learning and collaboration among programmers.

00:00:00.560 Okay, my screen isn't working, no problem. I'm using Vim, so you don't need to worry about that.
00:00:07.919 Okay, so my name is Ju. I want to do a very easy sort of talk today, just one simple exercise.
00:00:15.799 You know, we have a lot of programmers in the room, so we should be able to handle this exercise, right? It's not too difficult.
00:00:24.080 We're going to do it together and see how what seems to be an easy problem can be quite hard to get right.
00:00:29.160 Then, I would like to try solving the same problem using some patterns and techniques from the functional programming world.
00:00:34.960 I've been doing a fair amount of functional programming, so we can just take a look at it.
00:00:58.920 One thing I really wanted to make clear is to feel free to interrupt me at any point. No need for microphones; just yell if you see a typo or think we could do something better. I'm very happy to take it easy.
00:01:16.920 There's no script apart from this one.
00:01:23.640 So, let me set the scene. First of all, let's generate some random numbers in Ruby and save them to a file.
00:01:38.600 I'm going to say 'store' and just save it as 'numbers' so we can refer to them later. I'll generate 100 random numbers using 'map' with 'rand 10'. Once I've done this, I'll join them with a newline character.
00:01:56.360 And that's it.
00:02:01.920 To keep the exercise meaningful, I'll do it only once. So I'm going to say 'unless file.exists? store'.
00:02:21.840 You see, no copilot here, it's all done manually.
00:02:32.480 Now that I have this, I'm going to run the script.
00:02:43.959 If I look, we have some numbers. If I run 'head of numbers', tada! Random numbers!
00:02:55.080 So, we have these random numbers. What do we want to do with them? As I said, it's a very simple exercise; we're going to create a histogram.
00:03:01.959 For each of these numbers, I want to write the index of that number and then as many hashes as the number itself.
00:03:08.920 So for the first number, we write zero hashes; for the second number, we write one hash, and if there are eight hashes, we write eight hashes.
00:03:16.360 Let's do that.
00:03:22.239 So, I'm going to actually read these numbers. I'll use 'file.read store', and I can now split them over newline characters.
00:03:28.120 I can do something a little tricky, which is to use a magic proc. Basically, you can write a block without actually indenting the block. You can say, 'I want to map over this function' and just pass in the symbol.
00:03:41.200 Anyways, I've spoken too much. You read the files, split them over the new lines, and convert them to integers.
00:03:59.680 Now, once I have that, I can loop through each number using 'each_with_index', which is part of the Enumerable module. You will get the value plus the index, which is convenient.
00:04:15.319 So, I get the number and the index; for each of these, I can say I want to print the index.
00:04:20.519 Then, for each of my numbers, I can say the hash symbol and repeat it as many times as the number itself.
00:04:25.639 This is one of the beauties of Ruby. Now if I run the script, tada!
00:04:36.600 When we check the numbers, it looks about right: six here, seven, five. Yeah, it seems correct.
00:05:05.160 Any questions about this code? It's pretty easy, right? Let's make a little change and see how much more difficult this exercise gets.
00:05:20.000 The one change I'd like to make is to group consecutive rows that have the same number and write a nice summary. For example, on rows 93 and 94, I would like to display a summary instead.
00:05:36.360 Instead of just showing this directly, I would like to show something like 'D94' instead.
00:05:52.319 How would you do it?
00:05:58.479 Think about it, but in silence, like this very old ancient Chinese proverb.
00:06:01.479 Okay, I'm sure you have some ideas. Start talking, but for now, think about it.
00:06:07.520 We're going to try to solve it in a random way, and I think it would be fun to use just imperative code. We're going to rely on good old variables, and we will do some messy stuff to see how we can arrive at a solution using traditional imperative code.
00:06:36.360 What makes this problem different and more complicated is that we have introduced some sort of state. Previously, we could just consider each row individually, but now we have to delay actions until that group of the same number is fully processed.
00:06:47.400 So if I were to be explicit about it, I would say, 'Okay, I need to keep track of the previous number and only print when needed'.
00:06:54.600 If the current number is the same as the previous, I know the group is continuing, so I will continue without printing. If the current is different from the previous, I can print a summary of the previous group.
00:07:19.799 Now, I will say that the previous number is initially nil, and we'll start going through the numbers using each with index. The first problem we face is that if the previous number is nil, we can't compare it directly.
00:07:40.000 So we have to initialize it with the current number for the first iteration. In the next iteration, we will check if the current number equals the previous number.
00:08:09.440 If they are the same, we can just continue. If they are different, we can print a summary of that previous group.
00:08:38.760 We forgot to keep track of the previous index, which is important because for example, in the group, we should have noted 93, but we forgot to do that. No problem, we can just add a new variable for the previous index.
00:08:58.120 This is the beauty of programming. You create things on the fly.
00:09:23.600 So we set previous_index to the current index. Now, I'll say that if the previous_index equals index minus one.
00:09:52.560 If it is a group of only one, we will print something slightly different with the correct formatting.
00:10:00.800 We will also have to ensure we handle the last group correctly.
00:10:10.960 We always print the previous group, so once the iteration is done, we need to ensure there are no lingering values in the temporary variables.
00:10:41.840 If previous_index is equal to last_index, this is enough. Otherwise, we need to print the more complicated version.
00:11:03.680 Okay, let's try running this. All looks good; commit and push!
00:11:40.000 Now, any observations? We just spent some time on a piece of code that looks like this, and I've tried ChatGPT to generate similar code, which often results in very similar specifications.
00:12:02.000 However, I am quite unhappy with how this code looks. After writing it together, I've already forgotten the numerous problems we encountered.
00:12:32.000 If we consider this code a bit more critically, we could evaluate its problems without changing it. We could say it has high cognitive complexity and is, indeed, ugly.
00:12:49.360 When we talk about functional programming, we often think about how it helps us write cleaner, more understandable code by avoiding mutations and state.
00:13:14.000 Imagine just having a fritto misto like deep-fried fish for lunch and then trying to digest all this code.
00:13:24.000 This demonstrates how much brain power is consumed by intricacies in imperative code. We can approach it differently by transforming our data.
00:14:08.000 So, let’s talk about the vital principle of functional programming and how it focuses on data transformation. Instead of printing while coding logic, we can separate these two concerns.
00:14:55.200 Everyone has written code that resembles the imperative version— it's practical, but we often need to represent logic for future understanding.
00:15:18.000 Thus, discussing the data transformation approach allows us to consider how we can use it to create intuitive logic instead.
00:15:44.000 Now, let’s implement a clear data structure that can address our tasks directly. This way, we will handle how we would approach printing, keeping track of elements and values.
00:16:30.000 We create pairs of numbers and indices; this way, transformation comes first. Our issue of needing to print can then happen on a separate method.
00:17:17.300 I will use each_with_index for my original list and establishes groups using functional principles.
00:17:50.000 Using a tuple here will help manage the transformation, emphasizing what we are trying to accomplish with greater clarity.
00:18:19.000 Now that we've established this pairing concept, let’s focus on grouping based on previous numbers and handle necessary conditions.
00:19:03.000 If we maintain clarity so that the data transformation aspect acts independently from the output-dependent logic then it will simplify change management in future revisions.
00:19:40.000 It's critical we focus on how to make sure functionality isn't overlooked due to lack of cleanliness in code.
00:20:00.000 After restructuring the logical partition, we can acknowledge that the requirements regularly change and updating the transformation aspect can be done rapidly.
00:20:32.000 To summarize, recognizing how functional programming frameworks can assist us in not just clarity, but efficiency in translation to practical implementations becomes a distinct advantage.
00:21:05.000 In five minutes, let’s touch briefly on how we can understand clearer programming paradigms through conversation and integrated frameworks.
00:21:45.000 The goal is to use functional methods rather than imperative to enhance understanding.
00:22:10.000 Prompt transformation will help deepen both individual comprehension as well as collaborative processes.
00:23:00.000 By adhering to these conversion aspects, we can leverage distinct programming advantages that functional programming provides.
00:24:00.000 If we consciously re-examine our inquiries in development, through every iteration and prompt, we reach more fluid, flexible, and robust foundations.
00:25:00.000 Every step we take towards functional principles affords us incredible avenues for growth and understanding. It’s this machine of thought that can elevate our development with elegant solutions.
00:26:00.000 Let’s keep discussing how implementation through conversation and logic can be assisted by shared practices and clean structures.
00:27:20.000 When working with structures that evolve, it’s crucial to be able to denote patterns to facilitate clear epochs in code writing.
00:29:00.000 Thanks for participating; I hope these concepts offer you guidance in navigating your coding environment and extracting value from logical structures.
00:31:00.000 Let's transition to some live demos, culminating in a Q&A.
00:32:00.000 By allowing time for inquiry, we can explore even more dimensions of our practice.
00:33:00.000 Thanks for your contributions and attention throughout the discussion!