Complexity Management
Representations Count
Summarized using AI

Representations Count

by Tom Stuart

In the presentation titled "Representations Count," Tom Stuart, CTO of FutureLearn, explores a unique problem involving negative numbers in an old version of Ruby that does not support them. The talk, given at RubyConf AU 2019, leads the audience through an engaging fictional scenario where one discovers a vintage computer that limits numerical calculations.

Stuart presents a challenge: how to implement support for negative numbers on this outdated system. He emphasizes the importance of selecting the right representation for signed numbers. The key points discussed in the presentation include:

  • Problem Description: The vintage Ruby implementation generates errors if an operation leads to a negative number. The challenge is to create a class that can handle signed numbers, allowing for operations to yield correct results without errors.

  • Base Representation of Signed Numbers: The class requires a sign and an absolute size for the numbers. Stuart proposes using symbols for signs while ensuring the size remains a positive integer to simplify computations.

  • Creating Factory Methods: The first step involves implementing factory methods for creating positive and negative numbers, which dictates how instances of the class are initialized with proper attributes.

  • Equally and Addition: The presentation details the equals method, which checks if two signed numbers are equivalent based on their size and sign. Stuart introduces the plus method, where errors must be handled when adding numbers of differing signs, particularly avoiding negative results in a non-negative supporting environment.

  • Conditionals for Error Handling: He explains the need for conditionals that check if subtraction is valid when combining positive and negative numbers, ensuring that they stay within the acceptable range.

  • Representation of Zero: An interesting discussion unfolds on handling zero in terms of its representation, leading to conclusions that clarify it should not be treated as a special case despite its signs differing (positive zero vs negative zero).

  • Exploration of Multiplication: Stuart extends the exploration to multiplication, suggesting geometric representations to conceptualize multiplicative operations without relying on subtraction, thus emphasizing elegance in code design.

  • Design Decisions and Real-World Implications: The presentation closes by underlining the significance of thoughtful representation choices in programming, encouraging developers to consider implications of design decisions in their work.

Finalmente, Stuart's talk highlights that choosing the correct data representation can simplify operations and reduce cognitive load for programmers. By engaging the audience with practical implications, he fosters a deeper understanding of the complexities involved in numerical computations in programming.

00:00:00.030 Next up, we have Tom Stuart. Tom traveled here from London and is the CTO of FutureLearn, which is an online learning platform. Previously, he was doing his PhD at Cambridge, which he assures me is superior to Oxford. However, he didn't actually complete his PhD because he found the tutoring and lecturing side too interesting to finish his thesis. Instead, he wrote a book about it. So, welcome Tom Stuart, as it is his second time here.
00:00:54.880 Hi everyone! I'm so happy to be here in this beautiful city at this fantastic conference again. Thank you so much for having me. Imagine this: you're at your grandparents' house, and in the attic, you find a dusty old computer. You plug it in, and it boots up — it still works! It turns out that it runs Ruby, but when you fire up IRB, you realize there’s something a little strange going on. You can multiply numbers no problem, and subtraction works fine sometimes. However, if you attempt to perform an operation that might generate a negative number, you receive a strange error: zero is the smallest number. It turns out that this version of Ruby doesn't support negative numbers.
00:01:46.720 So here’s your challenge: how will you add support for negative numbers on this old computer? More specifically, how are you going to implement a class that can represent signed numbers? You need to create numbers that can be either positive or negative, with some factory methods for building positive and negative numbers, and a few instance methods for basic arithmetic. If you implement this class, you could require it in IRB and define some helper methods to make it more convenient to call those factory methods. Then, you would be able to add numbers and get the correct answer, or safely subtract numbers to receive a negative answer without any errors, or multiply positive and negative numbers, or compare them.
00:02:37.870 Let's start by looking at the basic form of a signed number. It has a sign and what I'll call a size, which is its absolute magnitude. This size is always a positive number, so we can safely work with it on this old computer. We can begin by implementing those two factory methods — one that takes a number and creates an instance with the size that corresponds to the number passed in, setting the sign depending on which factory method is invoked. We can represent signs in various ways; I've chosen symbols, but you could use booleans or create your own sign class. In the initializer, we will store the sign and size in instance variables and provide getter methods so that other instances of this class can access these values.
00:03:38.050 Next, let’s implement the equals method. If I'm given another signed number, how do I determine if it's equal to mine? We just check that the sign and the size of the other number match mine. According to IRB, this works fine: equal positive numbers are equal, and equal negative numbers are equal. If the sizes differ, then those numbers are not equal, and if the signs diverge, they're also not equal. Now, let's move on to the plus method. Begin with the simplest approach: keep the sign as is and add the sizes together. This approach works well for both positive and negative numbers.
00:04:59.660 However, adding a positive and a negative number gives the wrong answer. For instance, if we add positive 2 and negative 3, our computation should actually have subtracted instead of added. We need to put a conditional around this part. If the signs are the same, go ahead and add the sizes; if they are different, subtract the sizes. Now, a positive plus a negative works, as does the reverse. Let’s try another example: when we add positive 1 and negative 3, our subtraction fails when attempting to go below zero, which this version of Ruby cannot handle. We need to guard against this situation with another conditional.
00:06:30.490 To handle this, we check if the size of the first number is sufficiently large for the result to be zero or greater. If it is, we proceed with the subtraction; if not, we subtract in the opposite direction and flip the sign of the result. This approach allows us to handle situations where the result could be negative correctly, either way, be they negative or positive. Let’s perform a final check to ensure everything works properly when the result is zero. If the result of positive 3 plus negative 3 is positive 0, that seems fine, but we must address how positive and negative 0 are treated since they should be equal despite differing signs.
00:07:49.160 This part of the story is complex due to the various edge cases we need to handle. By observing that we have multiple representations, we can eliminate those special cases. Every number can have more than one representation, thus allowing us to avoid treating zero as a special case. This representation underscores a regularity that our first method lacked; this new method simplifies our operations and avoids numerous edge case considerations. Visualizing numbers as differences enhances our understanding and helps refine our operations such that we can easily bring together disparate methods for complex calculations.
00:09:03.640 Let’s now explore how we multiply using this new representation. To multiply positive 4 and positive 5, we can visualize the problem geometrically. For example, we can view positive 4 as the difference between two heights, say 2 and 6, and positive 5 as the difference between 3 and 8. Our aim is to compute how to form the product while avoiding direct subtraction. Instead, we can derive from what we have; by strategically manipulating the existing variables and calculating products without direct subtraction, we quickly arrive at the appropriate results.
00:10:44.950 Ultimately, what’s important is the shape of the code, which should ideally be clean and free of complex conditionals. This significantly eases our problem-solving process because it reduces cognitive load and potential error. The fairy tale I presented is to illustrate the concept that while the story had challenges, it also teaches us valuable lessons about representation when approaching programming problems. In the world of programming, choosing your representation wisely is crucial, as improper choices can make seemingly simple tasks much more complex. I hope this has stimulated your thinking about the implications of design decisions in real-world applications, encouraging a deeper understanding of the problem space. Thank you very much for your attention. Enjoy the rest of your conference!
Explore all talks recorded at RubyConf AU 2019
+10