Implement keyword (instead of override)

Hi there,

so there is this question whether it’s “good style” to always use override when implementing abstract members. The advantage is that the compiler will throw an error if you accidentally try to implement a member that doesn’t exist. The big disadvantage IMHO is that the name override is badly chosen here.

So I wonder what people think of using a different modifier for this, for example implement:

object StringNoCase extends Ordering[String] {
  implement def compare(a: String, b: String): Int = 
    a.toUpperCase compare b.toUpperCase
}

This makes it more clear, and an additional rule could be that omitting that modifier results in a compiler error, so

object StringNoCase extends Ordering[String] {
  def compare(a: String, b: String): Int = 
    a.toUpperCase compare b.toUpperCase
}

would be forbidden.

Thoughts?

6 Likes

“implement” doesn’t seem any more clear to me, since every non-abstract def is implemented. In object oriented programming languages this is a very common usage for “override.”

7 Likes

But you can also override an abstract method with another abstract method, or override a concrete method with another concrete method. The implement keyword seems to only fit the case where a concrete method overrides an abstract one.

4 Likes

I agree with this proposal. We could experiment first with a @implement annotation maybe?

The problem I see with the status quo is not just that override is badly chosen, but that it can have unintended consequences: if the abstract method evolves and become non-abstract, then the compiler will not tell you that your implementation now overrides an already implemented method.

In summary, by writing override when implementing methods you get a compilation error if you implement a method that was not defined in the parent types, but you don’t get an error if you try to implement an already implemented method. By contrast, by omitting override you get an error if you try to implement an already implemented method, but you don’t get an error if you implement a method that was not defined in the parent types.

An implement keyword (or @implement annotation) would raise an error if you try to implement an already implemented method, and also if you implement a method that was not defined in the parent types.

3 Likes

That sounds like you’re suggesting implements alongside override, is this a correct understanding?

I’d be in favor of that.

1 Like

This is precisely why I generally do not use override in this situation. I’ve always found the preference to use it there a bit mysterious, since it seems to cause more difficulty than benefit. (I don’t personally find the status quo to be a problem, probably for that reason.)

1 Like

What’s wrong with a concrete method overriding another concrete method?

Nothing, provided that’s what you’re expecting.

If you’re providing an implementation to a method which is omitted, and the library designer provides a concrete implementation, it’s generally the sort of think it’s good to be made aware of.

Now, it could be that there isn’t anything which you’d need to do, other than replace implements with override, but there could be subtle behavioral differences the rest of the class expects that would be a pain to track down, and getting a heads up that the method contract had changed could be quite useful.

I’d be happy with implements alongside override. I’d be less interested in removing override.

5 Likes

Yes

Too many keywords. Such a fine distinction is not worth remembering a new keyword. Certainly not in the same universe in which private[this] & protected[this] got the axe for adding meaningless distinction

4 Likes

Let’s agree that OO inheritance contracts are broken and get rid of override.

Either overriding a concrete definition is brittle and it should be final, or it should be marked open for anyone who cares to override it.

I don’t really care if I’m overriding a trivial definition of close(), for example, or that I’m implementing an interface that extends Closeable or Autocloseable, etc.

If I have to care, then the superclass should define a better interface with proper extension points, and tell me exactly what I must implement.

Warnings could be emitted in suspect cases (similar to the proposal for open classes), unless language.wildwest.

During refactoring and migration, the build tool can tell me if I accidentally changed overriding.

4 Likes

These are all ways in which library authors could do better, but it doesn’t help the library users that currently have a rock-and-hard-place choice around override.

I’ve long wanted to an alternative to override, as @julienrf summarised, but what @kai says about too many keywords is also right…

Would someone be up to creating a schematic of the different cases and how override/implements/override implement would behave? It would be helpful.

4 Likes

If you want to make sure your method implementation does not override another implementation, what about mixins? Or would this be limited to classes only, not traits?

1 Like

No.

1 Like

If it is allowed for traits, it does not achieve the goal of preventing one implementation to override another implementation.

1 Like

Perhaps concrete and overwrite:

  • concrete definition implements abstract method
  • overwrite definition replaces implemented method
2 Likes

Do you mind showing me the kind of things you think wouldn’t be handled?

Would this be legal?


trait A {
  def name: String
}

trait B extends A {
  implement def name: String = "Alice"
}

trait C extends A {
  implement def name: String = "Bob"
}

class D extends B with C {
 
}

object D {
  val d: D = new D
  println([d.name](http://d.name))
}

If this is legal, then what is the point of the implement modifier?

1 Like

You’d have to write abstract implement, the same as abstract override.

My question is, instead of override def why isn’t it just redef f() = ???.

The word in common parlance is “redefine”, pardon my French.

2 Likes

But these are not abstract methods.