I was intrigued by how this issue might be approached with dependent types, so I installed Idris2, read docs for an hour or two and came up with something a little different that what I was imagining, but somewhat interesting nonetheless.
I didn’t find out if I could write a function like forall (n m: pos) where n > m -> (sub n m : pos)
But I did manage to do was this: (I defined positive integers as a unary type because I don’t know anything about type aliases in Idris yet)
subType : Pos -> Pos -> Type
subType One One = Nat
subType _ One = Pos
subType One _ = Integer
subType (S n) (S m) = subtractable n m
subPos : (n : Pos) -> (m : Pos) -> subType n m
subPos One One = 0
subPos One (S x) = -(natToInteger (natOfPos x))
subPos (S x) One = x
subPos (S x) (S y) = subPos x y
Basically what you can do is create functions which return types, like subType
above, and then use them in type signatures for other functions (as in subPos
)
So here, subPos
may return an Integer, a Nat or a Pos based on the value of the inputs. Kind of an interesting approach. Idris can confirm that this is correct because the case matching between the two functions is basically identical, so it doesn’t need to actually execute the code for subType at runtime to determine the type because it can statically deduce that it is correct.
What I don’t yet know is what you can do with the output of this function in a case where the values of the inputs are unknown. I assume you have to handle every case, but I don’t know what that looks like yet.