0

I'm working with a function that takes a list of email address records and returns the appropriate one for the use case. This list is passed to the function as an List[Map[String, String]], and the first step of the function is to cast each of the Map[String, String] members to a custom EmailAddress case class. Essentially, the relevant code looks like this:

val emails = List(Map("email_normalized" -> "email@address.com"))

object CaseClass {
  def identifyBestEmail(emails: List[Map[String, String]]): Map[String, String] = {
    val emailsCast = emails.map(emailFromMap)
    bestEmail(emailsCast)
  }

  def emailFromMap(x: Map[String, String]): EmailAddress = {
    EmailAddress(email_normalized = x.get("email_normalized").asInstanceOf[Option[String]])
  }

  def bestEmail(x: List[EmailAddress]) = {
    Map("email_normailzed" -> "the best")
  }

  case class EmailAddress(email_normalized: Option[String])
}

CaseClass.identifyBestEmail(emails)

I'm new to Scala and just learned about implicits, and I feel like there has to be a way to simply this to something like:

val emails = List(Map("email_normalized" -> "email@address.com"))

object ImplicitClass {
  def identifyBestEmail(emails: List[EmailAddress]): EmailAddress = {
    Map("email_normailzed" -> "the best")
  }

  implicit class EmailAddress(emailMap: Map[String, String])
}

ImplicitClass.identifyBestEmail(emails)

This latter example seems to me like it is much simpler, more flexible and easier to read than the previous one. However I can't figure out if there's a way to actually get it to work. When I try to run the code I get this error message:

error: type mismatch;
 found   : List[scala.collection.immutable.Map[String,String]]
 required: List[ImplicitClass.EmailAddress]

Am I on the right track here? Is there a cleaner way of handling the use case than what's in the first example?

UPDATE 1

This seems to work:

val emails = List(Map("email_normalized" -> "email@address.com"))

object ImplicitList {
  def identifyBestEmail(emails: EmailAddressList): Map[String, String] = {
    Map("email_normailzed" -> "the best")
  }

  implicit class EmailAddressList(emailList: List[Map[String, String]])
}

ImplicitList.identifyBestEmail(emails)

So I guess that means implicit conversions do not work on generic classes, you need to operate on the top level class.

James Kelleher
  • 1,957
  • 3
  • 18
  • 34
  • 1
    In your 2nd piece of code, the fact that the class is implicit doesn't bring any value. And the conversion logic of 1st code is not present anymore. IMHO this is not a use case for implicit classes, think of it more like extensions classes. Anyway you can just `emails.map(EmailAdress(_))`. – Gaël J Jan 12 '23 at 18:03
  • @GaëlJ I see, yea I thought that an implicit class would get me the conversion logic "for free", or at least allow me to put the conversion logic inside of the class definition – James Kelleher Jan 12 '23 at 18:16
  • You can use an `apply` method in the companion object for that. – Gaël J Jan 12 '23 at 18:40
  • 3
    I understand that you are new to Scala, but using `Any` is a really bad place to start. Why can't it be `Map[String, String]`? It would simplify the later code a lot. – Tim Jan 12 '23 at 19:28
  • @Tim oh that's just the way the code was written. fyi the function I'm refactoring actually takes an `Option[List[Map[String, Any]]]` – James Kelleher Jan 12 '23 at 21:12
  • @JamesKelleher But there is still `Any` in `Option[List[Map[String, Any]]]`. Why do you need `Any`? – Dmytro Mitin Jan 12 '23 at 22:22
  • @DmytroMitin the `Any` really is not what I'm asking about here but I'll find and replace it if it means we can refocus on the actual question – James Kelleher Jan 12 '23 at 22:29
  • The confusing and inconsistent types make it difficult to work out what the actual question is. Converting `Map[String, String]` to `EmailAddress` is trivial (`EmailAddress(map.get("email_normalized"))`) so what is the actual difficulty here? – Tim Jan 12 '23 at 23:06
  • to tell you the truth I think I figured out the answer as mentioned in the update, but if there's a way to implicitly convert the generic class I wouldn't mind knowing about it – James Kelleher Jan 12 '23 at 23:15
  • I've updated the title to address the root question – James Kelleher Jan 12 '23 at 23:19
  • 1
    https://stackoverflow.com/questions/38200682/chain-implicit-conversion-of-collection – Dmytro Mitin Jan 13 '23 at 01:08

0 Answers0