I’ve been meaning to bring this up for a while. Consider the following code:
case class Data(int: Int)
def genericExampleFn[A, B](f: A => B) = ()
val great = genericExampleFn(Data)
So far, so good. Now imagine that code and much more like it is committed into a codebase and the world moves on. …Then later someone adds the following:
object Data {
val empty = apply(0)
}
Now all those working instances of code similar to our val great = genericExampleFn(Data)
example no longer compile. It’s quite a frustrating experience, especially for juniors who have no idea why or how they’ve broken the codebase.
The reason is that fully-generated companion objects extend (A => B)
, where as when one adds their own companion object, they obviously don’t write extends (A => B)
themselves (or maybe AbstractFunction[A,B]
- one of the two) and so that breaks causes where the companion object was being used as a function. Quite a common scenario.
I’d love to see this improved. My suggestion is when an object X
(that isn’t a function), is supplied in cases where a function is required, rather than immediate rejection like we have at the moment, the compiler should also try X.apply
. How would it work in the case of overloaded apply methods? I’m proposing a tiny AST rewrite so the behaviour would be identical to whatever would happen if the user had written X.apply
instead of just X
.
Thoughts?