Two proposed changes for extension methods

Some new thoughts on the second issue: what syntax to choose for a direct call to an extension method. To recap: Given

object obj:
  extension (x: A) def fun (y: B) = ...

How do I call obj.fun directly, without using it as an extension method? This matters for two reasons:

  1. It’s a way to disambiguate things for the programmer if the extension method is not found at all, or the wrong one is found.
  2. It’s a way for the language definition to describe what an extension method application means.

Elaborate extension method call syntaxes like the ones we have been discussing address only the first aspect. They don’t solve the second, since we still have to explain what those elaborate syntaxes mean.

By contrast, the extension_fun name mangling provides a solution for both aspects. On the other hand, the extension_ name mangling has problems on its own. When do you use the extension_ name, when the normal one? It’s all a bit arbitrary.

So maybe we can do without extension_? An alternative rule would simply state that an extension method like fun above would translate to a method

object obj:
  <extension> def fun(x: A)(y: B) = ...

The <extension> modifier is not accessible to user programs. Such methods can be called like any other methods. So a.fun(b) would translate to obj.fun(a)(b), which is by itself a legal expression.

There are several tricky aspects about this, but I believe they can be solved.

  • We have to make sure that overrides respect extensionality. Only extension methods can override
    other extension methods, and all overrides of extension methods must again be extension methods.

  • Direct calls to extension methods must be selections with a qualifier and a dot. To see why, consider a collective extension like this one:

     object obj:
       extension (x: A) 
         def fun (y: B) = ...
         def other = fun(B())
    

    Here, the fun(B()) call in other expands to x.fun(B()). So it cannot be a direct call. To make it a direct call, you’d have to write obj.fun(a)(B()).

  • This means we cannot call an extension method directly at all if it is locally defined. Take the example above but now in a def instead of in an object:

     def outer(a: A, b: B) = 
       extension (x: A) 
         def fun (y: B) = ...
       a.fun(b)    // has no direct equivalent
    

    We cannot call fun directly, since there is no prefix from which we could select a fun. This is a hassle primarily for the language definition, where we’ll have to do some handwaving or resort to more awkward notation. For programming I don’t think it will matter, since it is an edge case of an edge case of an edge case. Direct calls of extension methods are already the rare exception. Local extension methods will be also quite rare, even though I see them to be useful on occasion. But if I have a local extension method, I don’t usually need to call it directly, since it is the thing that shadows all other possibilities anyway. So I can’t really see a scenario where one would write a local extension method that needs to be called directly. In that case, one should have defined the method as a normal method in the first place.

If we follow this route, we still have to decide whether extension should be a hard or a soft keyword. I am sitting on the fence here. On the one hand, making it a hard keyword is cleaner since it makes every word that can start a definition a hard keyword. On the other hand this will cause considerably breakage (starting with all code that calls extension on a file or a path). So, not sure about this one.

4 Likes