Type mismatch with Scala implicits

Asked by At

I'm trying to use implicits in Scala.

object TypeClasses extends App {
  trait HTMLWritable {
    def toHTML: String
  }

  case class User(name: String, age: Int, email: String) extends HTMLWritable {
    override def toHTML: String = s"<div>$name ($age yo) <a href=$email/> </div>"
  }

  val john = User("John", 32, "[email protected]")
  trait HTMLSerializer[T] {
    def serialize(value: T): String
  }

  object UserSerializer extends HTMLSerializer[User] {
    def serialize(user: User): String = s"<div>${user.name} (${user.age} yo) <a href=${user.email}/> </div>"
  }

  implicit class HTMLEnrichment[T](value: T) {
    def toHTML(serializer: HTMLSerializer[T]): String = serializer.serialize(value)
  }

  println(john.toHTML(UserSerializer))
}

this code won't compile:

Error:(41, 23) type mismatch;
 found   : lectures.part4implicits.TypeClasses.UserSerializer.type
 required: Int
  println(john.toHTML(UserSerializer))

I'm having trouble understanding the message, because according to IntelliJ, john.toHTML is a call to the toHTML method on the HTMLEnrichment class, which expects a HTMLSerializer, which is what I have given it. Nowhere have I defined a toHTML method which requires an Int.

1 Answers

4
flavian On Best Solutions

It's because you've accidentally overloaded the toHTML method. The error you're getting is because String.apply returns the character at a given index, which is why you are getting that error about the Int.

Intelij is not always efficient at picking out this kind of shadowing. It's a good idea to keep implicit mechanisms away from domain model, e.g de-couple specialised serialisations into implicits, just like you are doing with:

  implicit object UserSerializer extends HTMLSerializer[User] {
    def serialize(user: User): String = s"<div>${user.name} (${user.age} yo) <a href=${user.email}/> </div>"
  }

Then remove everything from your user, and maybe add a helper.

trait HTMLSerializer {
  def toHTML: String
}

object HTMLSerializer {
  // if you put this here you don't need to explicitly import it.
  implicit class HTMLEnrichment[T](val value: T) extends AnyVal {
    def toHTML(implicit serializer: HTMLSerializer[T]): String = 
      serializer.serialize(value)
  }
}

This means you can simply get the same effect as having a companion method, but you can keep everything nicely de-coupled, and you don't risk having those kinds of shading effects.