Type inference at module boundaries

Posted by    |       Ceylon

Gilad Bracha writes.

Wait: we are blessed, and live in a world where the gods have bestowed upon us such gifts as type inference. What if I don’t have to write my types at all, and have the compiler just figure out what types I need? The problem is that inference only works well within a module (if that). If you rely on inference at module boundaries, your module leaks implementation information. Why? Because if you infer types from your implementation, those types may vary when you modify your implementation. That is why, even in ML, signatures are declared explicitly.

Nicely put. Type inference for a published declaration - one that is visible outside some kind of reusable unit, be that a module, package, class, whatever - has the potential to leak implementation details to clients, exposing a more specific type than what the code that reuses the unit should really be seeing. For example:

shared local paymentProcessor(Payment payment) {    //note: "shared local" is not legal in Ceylon
    switch (payment)
    case (is CreditCard) { return creditCardProcessor; }
    case (is Check) { return checkProcessor; }

The return type of paymentProcessor() should probably be something abstract like Processor, to abstract the internals of the module, but in fact what is being hidden by the use of type inference is that it is actually something more concrete, like CreditCardProcessor|CheckProcessor.

And if the reusing code also uses type inference, then you can end up in a crazy situation that the reusing code has a compiled-in dependency to a type that it should not have been seeing, that it never even explicitly mentions in its own source code! For example, in another module, we write:

local paymentProcessor = paymentProcessor(payment);

Here, paymentProcessor also gets the type CreditCardProcessor|CheckProcessor.

This is the original reason we went down the path of limiting type inference to non-shared declarations in Ceylon. It wasn't until I started implementing the type inference in the type checker that I discovered that this restriction had an extra benefit: that we can do all type inference in a single pass of the code.

Back to top