Operator overloading
kthompson opened this issue · comments
Hello! I really like your series on type inference. Thank you. I was wondering if you knew of any information on how to implement something like operator overloading ie if i have some method fn(x) = x + x
and my language has both int and float so therefore two ways to add, how would you go about inferring the type(s?) in that case?
Thanks 😄
Thank you 👍
So the first thing to note is that operators are just functions with a slightly different syntax.
Haskell provides overloading of functions via its type class mechanism:
class Num a where
(+) :: a -> a -> a
instance Num Int where
x + y = -- implementation for Int + Int goes here
instance Num Float where
x + y = -- implementation for Float + Float goes here
instance Num MyOwnType where
x + y = -- implementation for MyOwnType + MyOwnType goes here
-- ...
(Some things from the Num type class is omitted here for brevity)
This gives you an operator with the following signature:
(+) :: forall a. Num a =>
a -> a -> a
This says that for all types a
for which there exists a Num a
instance, a + a :: a
. E.g:
Float + Float :: Float
.
Int + Int :: Int
.
MyOwnType + MyOwnType :: MyOwnType
.
In the syntax of the series, we might have something like:
function f(x, y) { return x < x + y; }
Which should end up with a type signature like this:
function f<T>(x : T, y : T) : Bool where Order<T>, Number<T>
Given that the types of the operators are like this:
< : (T, T) => Bool where Order<T>
generic over T
+ : (T, T) => T where Number<T>
generic over T
So let's try our usual approach:
function f(x : $1, y : $2) : $3 {
return x < x + y;
}
When we get to x < (x + y)
, we can conclude from the signature of <
that we must require Order<$1>
.
When we get to x + y
, we can conclude from the signature of +
that $1 = $2
and that we must require Number<$1>
.
From return
we can conclude $1 = $3
.
Thus we have Order<$1>, Number<$1>
and $2 := $1, $3 := $1
. Generalizing the type we get:
function f<T>(x : T, y : T) : Bool where Order<T>, Number<T>
Now, how do we keep track of the Order<$1>, Number<$1>
?
For single parameter type classes like this, I believe the easiest approach is this:
- Keep a map from type variable to required type classes
- When binding one type variable to another, e.g.
$2 := $1
, transfer all the required type classes of$2
to$1
.
Nice. Thanks for the input. This is an interesting approach. When I was thinking about it I was thinking about it from a stand point that there would be two functions for the type ie x + y
being float or int but in a way you are essentially passing in a function that performs the addition via the typeclass. In that way we would only ever pass in a single way of adding but the types could still be generic.
I also looked at the other language you work on and I see how you use "constraints" which appear to essentially be the type classes. Very cool stuff. Anyways thanks for the feedback 😄