You can control type specialization to a certain extent with overloading. For instance, if a procedure takes two arguments, and if both these types can vary independently, then you can use overloading to ensure that both arguments are converted to a common type.
procedure foo;
[T1,T2]
overload foo(a:T1, b:T2) {
// by default, convert both arguments
// to the type of first argument.
foo(T1(a), T1(b));
}
// the following overload specializes for the case
// when both arguments are of the same type.
[T]
overload foo(a:T, b:T) {
...
implement the logic here
...
}