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
}
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.
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
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
^
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.