archives

Making implicits less powerful?

Many people feel that Scala implicits are powerful enough to replace all uses of Haskell type classes, but might be too powerful, because they make it too easy to shoot yourself in the foot.

The problem is caused by not just implicit values, but also implicitly invoked functions. These are necessary if you want to cover all the uses of type classes. For example, the statement "if values of type T can be compared, then values of type List<T> can be compared as well" corresponds to an implicitly invoked function that receives a Comparator<T> and returns a Comparator<List<T>>. Having implicitly invoked functions introduces an alternate model of computation into the language, allowing a simple form of logic programming. A similar thing happens in Haskell with type class constraints. The Agda designers decided against implicitly invoked functions for precisely this reason.

I was unable to find any consensus on how to make implicits weaker, to allow useful scenarios but prevent harmful ones. Scala has adopted several such restrictions, described in the links above. I feel that the Scala model might be still too permissive. Here's some more ideas for restrictions:

1) Require explicit importing of all implicit values defined in other modules, except the ones automatically defined by the language (e.g. Comparator<Pair<Int,Int>> or the function Comparator<T> -> Comparator<List<T>>). This might be too drastic though, especially if the language requires you to say "deriving Eq" and doesn't define stuff automatically.

2) Check for all possible ambiguities in implicit resolution, not just actual ones. Haskell's rules for instance contexts (7.6.3) are one way to make that decidable. In Scala I guess it's undecidable, but can't say for sure.

3) Allow only types whose definition is marked as "implicit" to be used as implicit arguments. For example, Comparator would be allowed to be implicit, but Int or String or function types wouldn't. This would also remove a potential ambiguity, because functions marked as "implicit" would be always treated as implicitly invoked functions, and never as implicit values.

Has anyone here given this some thought? What are the engineering considerations?