I'll talk about broken ways of building the rationals ℚ in Agda, how its desirable to be able to compare rationals for equality, how having proofs in the language can get in the way of that, and how to cope with that.
This is all based on the code I found in the Data.Rational standard library module, which I have been trying to understand as I want to use both the module itself, and its techniques, in a scientific units-of-measurement library that I am writing.
RationalsA rational (or a fraction) can be described as an integer (the numerator) divided by a strictly positive integer (the denominator).
It's not hard to build an Agda data type to represent a fraction as a pair of two integers:
data ℚ : Set where field numerator : ℤ denominator : ℤ
This implemenation doesn't require the denominator to be a positive integer, though. So we can represent "invalid" fractions. Most importantly, we can represent a fraction with denominator 0 this way, which is nonsensical.
So lets store the denominator minus 1 as a natural number (0, 1, 2, ...) like this:
record ℚ : Set where field numerator : ℤ denominator-1 : ℕ
The denominator minus 1 can be 0 or higher, which means the denominator can be 1 or higher - a positive integer as desired.
On the downside, now every time we want to compute with the denominator, we have to compute it first by adding one to the denominator minus 1.
This is a pattern I found strange (and still find unintuitive) in Agda. Rather than storing something in a format that you want (the denominator), you instead store it in a format that makes correctness work.
Read on for part II...