Ability to make a parent's concrete method abstract

I have found myself wanting to implement a method in a typeclass hierarchy in terms of another method, (that can be defined in terms of this one) and then make the original method abstract to avoid infinite loops. Would this be something that could be considered as a language improvement?

I have a very solid usecase of this at the moment, and the workaround is to override the original method in terms of a new method, with a new name (e.g. with an underscore suffix) and leave this new dummy method abstract. e.g.

trait Foo[A] {
  def foo: A = bar
  def bar: A
}

trait Bar[A] extends Foo[A] {
  abstract def foo: A
  def bar: A = foo
}

without the ability to define foo as abstract the bar would call the concrete foo and then recurse to a stackoverflow.

Workaround is

trait Foo[A] {
  def foo: A = bar
  def bar: A
}

trait Bar[A] extends Foo[A] {
  override def foo: A = foo_
  protected def foo_ : A
  def bar: A = foo
}
2 Likes

Can you show what you mean in code?

4 Likes

This might be a legit feature request.

As a workaround, I prefer a different approach:

trait Foo[A] {
  def foo: A
  def bar: A
}

object Foo {
  trait FromFoo[A] extends Foo[A] {
    def bar: A = foo
  }

  trait FromBar[A] extends Foo[A] {
    def foo: A = bar
  }
}
1 Like

How would this feature interact with Java? I could conceive of the Scala compiler requiring you to implement the abstract method; however, Java would not know about the method being abstract and would allow you to instantiate an instance of the subclass without implementing the abstract method.

1 Like

It seems like what you really want to specify is not that subclasses must override the original method, but that they must either override the original method or override the added method to avoid loops. The exact same problem exists in Haskell and was resolved with the addition of the MINIMAL pragma: https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#minimal-pragma

2 Likes

Perhaps I misunderstand, but can’t you just make foo final?

trait Foo[A] {
  final def foo: A = bar
  def bar: A
}

Or, to answer the subject verbatim, “make a parent’s concrete method abstract”.

scala> abstract class Foo {
     | override def toString(): String
     | }
defined class Foo

scala> class Bar extends Foo
<console>:12: error: class Bar needs to be abstract, since there is a deferred declaration of method toString in class Foo of type ()String which is not implemented in a subclass
       class Bar extends Foo
             ^
2 Likes

Okay, my mind is blown.

yes, this is exactly the same thing.

whaaat? That’s weird… it works for your example but it doesn’t work in general. e.g. in https://gitlab.com/fommil/stalactite/blob/coproducts/src/main/scala/scalaz/Coproductive.scala#L22 you cannot use your trick with apply2. Try it, you’ll get a stackoverlflow as ap calls apply2 which calls ap, although it compiles if you introduce the override def apply2 with the correct signature.