Search code examples
scalaimplicitoverriding

Scala implicits and overrides problems


Here is my problem:

trait Caller {

    type EntityType
    def parseEntity(entity: String): EntityType
}

trait IntCaller extends Caller {

    implicit def strToInt(s: String) = s.toInt
    override type EntityType = Int
    override def parseEntity(entity: String): EntityType = entity
}

trait DoubleCaller extends Caller {

    implicit def strToDouble(s: String) = s.toDouble
    override type EntityType = Double
    override def parseEntity(entity: String): EntityType = entity
}    

object main {

    def main(args: Array[String]): Unit = {
        val intCaller = new IntCaller{}
        val doubleCaller = new DoubleCaller{}

        println("Result is: " + intCaller.parseEntity("5"))
        println("Result is: " + doubleCaller.parseEntity("5.0"))
    }

}

As you can see I keep repeating the code for: parseEntity method. If I wanted to add a FloatCaller I would have to rewrite parseEntity even though its implementation would be the same.

How can I write the implentation of the parseEntity in the Caller, so that I don't have to write the same code in the child traits again and again?

Disclaimer: This is a simplification of a real problem I have with SprayJsonSupport from akka.http.scaladsl.marshallers.sprayjson.


Solution

  • You would be better off using a factory method that can build instances of a Caller given a conversion function. The only thing that's different between IntCaller and DoubleCaller is toInt and toDouble (and the types, of course).

    trait Caller {
        type EntityType
        def parseEntity(entity: String): EntityType
    }
    
    object Caller {
        def apply[A](f: String => A): Caller = new Caller {
            type EntityType = A
            def parseEntity(entity: String): EntityType = f(entity)
        }
    }
    
    scala> val IntCaller = Caller(_.toInt)   
    scala> IntCaller.parseEntity("123")
    res1: IntCaller.EntityType = 123
    
    scala> val DoubleCaller = Caller(_.toDouble)
    scala> DoubleCaller.parseEntity("1.23")
    res2: DoubleCaller.EntityType = 1.23
    

    If you want to keep using inheritance, then continue to force the sub-classes or traits to implement the conversion with parseEntity. It's not really necessary to using implicit conversions, though. The only reason it appears that there is repeated code is because the implicit conversion makes parseEntity look the same for each implementation, even though it's really not (because it needs to resolve a different implicit).

    trait Caller {
        type EntityType
        def parseEntity(entity: String): EntityType
    }
    
    trait IntCaller {
        type EntityType = Int
        def parseEntity(entity: String): EntityType = entity.toInt
    }