Specialization
Wipple doesn’t support overloading like many other languages. Instead, you can use a trait to describe how functions operating on the trait should behave for a certain type. For example, you can implement the count
function for every iterable type:
count :: Collection where (Iterate Collection _) => Collection -> Natural
count : ... -- iterate over each item and increment a counter
And if you implement Iterate
on, say, a List
, you get count
for free!
There’s one problem with this approach, and that’s performance. While count
’s current implementation (iterating over each item and incrementing a counter) is indeed the most generic and flexible way to do it, it’s very inefficient for types that already know the number of elements they contain. Let’s say we have a type called Repeat
that produces a value count
times:
Repeat : A => type {
value :: A
count :: Natural
}
A => instance (Iterate (Repeat A) A) : ...
Because Repeat
implements Iterate
, count
will work just fine. But we can eliminate a bunch of unnecessary work because Repeat
already knows its count
! To accomplish this, we can use specialization.
How does specialization work?
Specialization allows you to declare a new constant for a more specific type and tell Wipple to use it instead of the generic implementation. This is done using the [specialize]
attribute:
[specialize]
count :: Repeat _ -> Natural
count : { count } -> count
Now Wipple will use the specialized implementation of count
whenever it’s called with a Repeat
value.
Rules and conventions
Because specialization is intended solely for performance, it’s supposed to be invisible to the end user of your library. Therefore, there are some restrictions on specialized constants:
- They must have the same name as the generic constant.
- They must have the same type as the generic constant and satisfy all of its bounds.
- You can’t specialize a constant that is a specialization of another constant.
In addition, there are some conventions that Wipple can’t check, but you should follow:
- The specialized constant should have the same behavior as the generic constant; it should be a “drop-in replacement”.
- You should only specialize a constant you created, or your specialization should operate at least one type you created. This prevents conflicts between libraries.