i don't *care* how maths works, half of zero should be 0.5
This tweet promptly crawled inside my brain and sat there until I figured out how to make it work. I did, and because I’m so generous, now I’m going to share this secret with you. We can ignore what “mathematicians” say, and make division behave however we like.
Normally I use Python for code crimes, but numbers are built-in types, and modifying them seems quite hard. It is possible – for example, using Alt29 from adtac’s “exterminate” library – but that only allows us to replace the value of zero. I can’t think of a good way to change how it behaves without diving into the bowels of C.
One of my favourite talks is Kevin Kuchta’s Ruby is the best JavaScript, which thoroughly abuses Ruby features to write code which is simultaneously valid Ruby and JavaScript. I had a vague memory that it involves monkey-patching on classes – even built-in classes – which seems exactly what we’d want for crimes fixing mathematics.
We might even find some companions along the way.
First we need to find out the class of zero, so we know whose methods we’re going to override:
irb(main):001:0> 0.class
=> Integer
To override a method, we declare the class, then declare the method, and this replaces any existing implementations. For example:
class Integer
def /(divisor)
"headache"
end
end
puts 29018198 / 37 # "headache"
Thus recreating the experience of many people and school-level maths.
This gives us a clear way forward: we’ll override this function to return the result we want. If we’re doing 0 / 2
, we’ll return 0.5
, and otherwise we’ll return the normal result.
class Integer
def /(divisor)
if self == 0 and divisor == 2
0.5
else
self / divisor
end
end
end
puts 0 / 2 # 0.5
Hooray! We’ve fixed mathematics. Let’s just try 1 / 2
to check it still works for other numb—
Traceback (most recent call last):
16: from (irb):18:in `/'
15: from (irb):18:in `/'
14: from (irb):18:in `/'
13: from (irb):18:in `/'
12: from (irb):18:in `/'
11: from (irb):18:in `/'
10: from (irb):18:in `/'
9: from (irb):18:in `/'
8: from (irb):18:in `/'
7: from (irb):18:in `/'
6: from (irb):18:in `/'
5: from (irb):18:in `/'
4: from (irb):18:in `/'
3: from (irb):18:in `/'
2: from (irb):18:in `/'
1: from (irb):18:in `/'
SystemStackError (stack level too deep)
Oh.
Because we’ve replaced the /
method, we don’t have access to the old version any more – when we try to call it, it just keeps re-calling our new version. How do we perform the division when maths was already working?
We could cheat and use self.div(divisor)
, but I was planning to patch that too – we don’t want to leave old and broken methods lying around. That would be very careless; unbecoming of thoughtful and responsible citizens like ourselves.
How can we keep the old implementation of /
around so we can use it later?
Once I had this question in mind, I almost immediately stumbled upon a detailed Stack Overflow post by Jörg W Mittag. He explains why this sort of monkey patching is a bad idea and you should probably use inheritance instead, but we’re going to skip all that sensible advice to the bit where he explains how to make this idea work.
(Kate’s original idea is pretty easy if you’re allowed to create a new class and leave the Integer
class intact; the interesting bit for me here is being able to modify the built-in types. In particular, existing code with integers will pick up the new behaviour without modification.)
The trick is to save the instance method for division in a variable. This variable won’t be updated when we update the Integer
class, so we can call it to get the original implementation of division.
Like so:
class Integer
broken_div = instance_method(:div)
define_method(:/) { |divisor|
if self == 0 and divisor == 2
0.5
else
broken_div.bind(self).(divisor)
end
}
end
puts 0 / 2 # 0.5
puts 1 / 2 # 0
puts 3 / 2 # 1
This is looking pretty good – but mathematicians dream up all sorts of weird stuff. What if they have some special zero this doesn’t handle?
puts 0.0 / 2 # 0
We can fix this by doing a similar patching with the Float
class. Ruby also has Rational
numbers that we might want to patch, but I leave that as an exercise for the reader.
Let’s go ahead and put this all together.
We’ll redefine div
also; I leave divmod
and fdiv
as exercises (once you decide what the correct behaviour is in this new world). I don’t know why 1 / 2
and 1.div(2)
are independent; I thought maybe I could define one and the other would work automatically, but I couldn’t get it working. There’s probably a good reason I’m not seeing.
This is the final code:
class Integer
broken_div = instance_method(:/)
define_method(:/) { |divisor|
if self == 0.0 and divisor == 2
0.5
else
broken_div.bind(self).(divisor)
end
}
define_method(:div) { |divisor|
self / divisor
}
end
class Float
broken_div = instance_method(:/)
define_method(:/) { |divisor|
if self == 0.0 and divisor == 2
0.5
else
broken_div.bind(self).(divisor)
end
}
define_method(:div) { |divisor|
self / divisor
}
end
puts 0 / 2 # 0.5
puts 0.div(2) # 0.5
puts 1 / 2 # 1
puts 1.div(2) # 1
puts 6 / 2 # 3
puts 0.0 / 2 # 0.5
puts 1.0 / 2 # 0.5
puts 2.0 / 2 # 1.0
And that, I think, is enough. We’ve redefined what it means to divide zero in half, we’ve changed the way our computer thinks about division, and we have a template we could use to “fix” other operations. That banging on my front door is no doubt a group of excited mathematicians, keen to talk about what we’ve just done.
To be briefly serious: this post isn’t (just) taking a tweet far too seriously for the sake of a joke.
I learnt several new things about how Ruby classes and unbound methods work, mostly by guessing wrong and then reading the documentation to find out what I should have done instead. Although I read plenty of theory, I learn by doing, and even a silly project is still doing. I’m never going to use this code again, but I may well use this new knowledge.
You, on the other hand, definitely should use this code, ideally in production. Please send me a postcard to tell me how it goes.