0

I am trying to implement a trait that forces each class that extends it (and is not abstract) to implement certain methods (even if they already exist in super-classes). Concretely it should look like this:

trait Debugable {

  override def hashCode(): Int = ???

  override def equals(obj: Any): Boolean = ???

  override def toString: String = ???

}

That is the trait and this the implementation:

class TestClass {

}

object TestClass{
  def main(args: Array[String]): Unit = {
    val t = new TestClass
    println(t)
  }
}

The code above should ideally not compile (since a debugable class does not implement the required methods). In reality this does not only compile, but also throws no run-time exception (it just takes the default implementations of the object class).

Until now nothing managed to generate the expected behaviour. I think macros could help, but I am unsure if macros can express something like:

foreach class
     if class.traits.contains(debugable)
         return class.methods.contains(toString)

I know that I could let some external script do the check and have it bundled with the gradle compile task, but I am hoping for a solution which can be implemented as part of the project itself (since that would make it independent of the build pipeline used and since it should be simpler and easier to maintain/extend than writing a script crawling the entire source code)

3 Answers 3

3

This is close to it (and certainly an improvement over what I have), but it does not do exactly what I wanted. If I have a "chain" of classes, then it is enough for the top of the chain to implement the methods.

Typeclass approach can help with that, for example,

trait Debuggable[T] {
  def hashCode(v: T): Int
  def equals(v: T, b: Any): Boolean
  def toString(v: T): String
}

class Foo
class Bar
class Qux extends Foo

object Debuggable {
  implicit val fooDebuggable: Debuggable[Foo] = new Debuggable[Foo] {
    def hashCode(v: Foo) = 42
    def equals(v: Foo, b: Any) = true
    def toString(v: Foo) = "woohoo"
  }
  implicit val barDebuggable: Debuggable[Bar] = new Debuggable[Bar] {
    def hashCode(v: Bar) = 24
    def equals(v: Bar, b: Any) = false
    def toString(v: Bar) = "boohoo"
  }
}

import Debuggable._
def debug[T](v: T)(implicit ev: Debuggable[T]) = ???
debug(new Foo)   // OK
debug(new Bar)   // OK
debug(new Qux)   // Error despite Qux <:< Foo

Sign up to request clarification or add additional context in comments.

2 Comments

To be honest this is much better way of doing it than my. You don't need to extend anything and you can implement Debugable for types you don;'t have access to. My answer was just simple solution for question... this is proper one.
This is great. Just to clarify this for anyone reading reading this: Unlike the first solution the methods of the trait do not override the methods of the object this may or may not be a problem depending on the context
3

In my opinion you should just make it abstract.

// Start writing your ScalaFiddle code here
trait Debugable {
  def debugHashCode:Int
  def debugEquals(obj: Any): Boolean
  def debugToString: String

  override def hashCode(): Int = debugHashCode
  override def equals(obj: Any): Boolean = debugEquals(obj)
  override def toString: String = debugToString
}
//this will not compile
class TestClass extends Debugable { }
//this is OK but you need to implement 3 methods later :)
abstract class TestClass2 extends Debugable {}

https://scalafiddle.io/sf/bym3KFM/0

macros should be last thing you try.

1 Comment

This is close to it (and certainly an improvement over what I have), but it does not do exactly what I wanted. If I have a "chain" of classes, then it is enough for the top of the chain to implement the methods.
0

Based upon this I wrote the following, which satisfies my need and does override the default implementations:

trait Debuggable_Helper[T]{
  def hashCode(v: T): Int
  def equals(v: T, b: Any): Boolean
  def toString(v: T): String

}

trait Debuggable[T] extends Debuggable_Helper [Debuggable [T]]{
  override def hashCode(): Int = hashCode(this)
  override def equals(b: Any): Boolean = equals(this, b)
  override def toString(): String = toString(this)

}

class Foo extends Debuggable[Foo]{
  def hashCode(v: Debuggable[Foo]) = 42
  def equals(v: Debuggable[Foo], b: Any) = true
  def toString(v: Debuggable[Foo]) = "woohoo"

}
class Qux extends Foo with Debuggable[Qux] //does not compile


object Test{
  def main(args: Array[String]): Unit = {
    println(new Foo)   // OK - prints 'woohoo'
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.