0
votes

Suppose that I have the following code snippet:

import scala.language.implicitConversions

sealed trait Command {
  val typeName: String
}

object Command {

  implicit def command2String(implicit c: Command): String =
    c.typeName

}

case object SendMessageCommand extends Command {
  override val typeName: String = "send_message"
}

I would like to compare String with Command descendants without explicit conversion. For example:

"sendMessage" == SendMessageCommand

Q1: Is it possible in Scala?

Q2: Can I define generic implicit conversion that will convert known type such as String to super class types (like Command in my case)?

For example:

implicit def string2Command(implicit s: String): Command = {
  case "send_message" => SendMessageCommand
  case _ => // some error thrown
}

P.S. I know that it is not idiomatic Scala, but it would be awesome to find out the right way.

2

2 Answers

1
votes

Yes, its possible, but not with == because Scala support implicit conversion for these cases only: conversions to an expected type, conversions of the receiver of a selection, and implicit parameters and hence implicit is not applicable for comparison case. Instead, you have to create a comparison method in Command trait as below.

  sealed trait Command {
    val typeName: String
    def isEqual(c: Command) = c.typeName == this.typeName
  }

  object SendMessageCommand extends Command {
    override val typeName: String = "send_message"
  }

  implicit def stringToCommand(s: String) = new Command {override val typeName: String = s}
  implicit def commandToString(c: Command) = c.typeName

  val strToCmdCompare = "send_message" isEqual SendMessageCommand
  val cmdToStrCompare = SendMessageCommand isEqual "send_message"

  println(strToCmdCompare)    //print true
  println(cmdToStrCompare)    //print true

Now, coming to your second question, yes its possible to define a generic implicit conversion to convert any given type into another type instance. However, as I am understanding your situation, you have list of objects of Commands, and you want particular command within those objects for given string name. For this, you must have hold of those list of instance.

  sealed trait Command {
    val typeName: String
    //This is required for implicit conversion.
    override def toString: String = typeName
  }

  object SendMessageCommand extends Command {
    override val typeName: String = "send_message"
  }
  object AddMessageCommand extends Command {
    override val typeName: String = "add_message"
  }
  object UpdateMessageCommand extends Command {
    override val typeName: String = "update_message"
  }
  object DeleteMessageCommand extends Command {
    override val typeName: String = "delete_message"
  }

  //List of commands.
  implicit val cmds: List[Command] = List(SendMessageCommand, AddMessageCommand, UpdateMessageCommand, DeleteMessageCommand)

  //Convert given type T into type U.
  implicit def convert[T, U](s: T)(implicit list: List[U]): Option[U] = {
    list.find(_.toString == s.toString)
  }
  val res: Option[Command] = "add_message"
  println((res.getOrElse(null) == AddMessageCommand)) //print true

Note: as I am comparing two type instance in conversion by converting them into string, I have to override toString method in Command for this use case. You may need to do so for type T as well if it is not string. Furthermore, you can implement you own logic in convert method as per required instead of comparing with toString conversion.

0
votes

You can't do that with ==, but you can define your own operator:

 case class Foo(s: String) {
   def eq(x: Any) = x match {
     case Foo(str) => str == s
     case str: String => str == s
     cae _ => false
   } 
   def ==: (x: Any) = eq(s)
   def :== (x: Any) = eq(s)
 }

 "bar" ==: Foo("bar") // true
 Foo("bar") :== "bar" // true 
 Foo("bar") ==: Foo("bar") // true 

This works because of a special trick in scala, making methods, whose names end with a colon bind to the right rather than to the left: "bar" ==: Foo("bar") means Foo("bar").:==("bar"), not "bar".:==Foo("bar")

As for the second question, I am not sure I understand what you are asking ... "Can I define an implicit conversion?" Well, yes, you can :)