A postscript on traits and impls
4 October 2012
I was thinking more about type classes as I walked down the street. In my prior post I wrote that the rules I proposed resulted in a system where traits loosely fit the following Haskell template:
class C self a ... z | self -> a ... z where ...
However, I gave two caveats. The first was that due to subtyping we
cannot say that one type precisely determines another, but only that
it puts a bound. The second was that, in any given impl, the value of
a ... z
may be a type parameter which does not appear in the self
type. I think I understated the importance of this second caveat.
For example, consider the example I gave for simulating overloading:
trait Add<R,S> { pure fn add(&self, rhs: &R) -> S; }
trait IntRhs<S> { pure fn add_to_int(&self, lhs: int) -> S; }
impl<S, R: IntRhs<S>> int: Add<R, S> { ... }
This impl declaration essentially says “when self
is int
, the type
parameter R
may be any type which implements IntRhs
”. Moreover,
in this case, the self
type does not constrain the parameter S
at
all—that constraint is derived purely from R
.
In other words, while overloading-freedom does mean that the impl
which will be used is purely determined by self
, it does not mean
that self
alone determines the value of all the other trait
parameters, as my Haskell analogy implied. It’s more accurate to say
that the self
type determines a (possibly empty) set of bounds that
will be imposed on the other type parameters. These bounds can take
the form of subtyping bounds (lower- or upper-bounds, or both) or
trait bounds.