2

I have a function that should find command-line parameter with it's value and return converted to type P:

def parameter[P](name: String)(implicit tag: ClassTag[P]): P = {

    val paramName = s"--$name"

    args.sliding(2, 2).toList.collectFirst {
      case Array(`paramName`, param: String) => {
        // if P is Int => param.toInt
        // if P is Double => param.toDouble
      }
    }.get
  }

How do I do that? I've found that ClassTag is a way to go, but can't figure out how to use it in this case.

3 Answers 3

6

You can compare the class tag to tags for the types you want to match:

import scala.reflect.{ClassTag, classTag}

def parameter[P](args: Array[String], name: String)(implicit tag: ClassTag[P]): P = {
  val paramName = s"--$name"

  args.sliding(2, 2).toList.collectFirst {
    case Array(`paramName`, param: String) => (
      if (tag == classTag[Double]) {
        param.toDouble
      } else if (tag == classTag[Int]) {
        param.toInt
      } // and so on...
    ).asInstanceOf[P]
  }.get
}

You could also use pattern matching or whatever, of course. It works as expected:

scala> parameter[Int](Array("--foo", "123"), "foo")
res0: Int = 123

scala> parameter[Double](Array("--foo", "123"), "foo")
res1: Double = 123.0

There are lots of downsides to this approach, though—you have to know all the types you want to parse in the definition of parameter, etc.—and you're probably better off with a proper type class specifically designed for the kind of parsing you're doing.

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

2 Comments

Thank you for the answer. That works for me. Would accept the answer from @ColOfAbRiX as it looks a bit cleaner.
@YevhenKuzmovych I answered a similar question a few weeks ago, I used a similar approach as Travis. But, I used Options and the ClassTag's unapply method to ensure everything was typesafe - you may gave it a look, it may help you ;) - Anyways, as Travis already said (and as Tim said in that post), is always better to create your own typeclass for this kind of problems.
2

So Travis' and ColOfAbRiX's answers are working solutions. But as Travis noted, they are not typesafe.

Here is the solution I ended up with (see Scala Type Classes 101: Introduction):

Define implicit converters for types you need:

trait StringConverter[P] {
  def convert(a: String): P
}

implicit val string2string = new StringConverter[String] {
  def convert(a: String): String = a
}
implicit val string2double = new StringConverter[Double] {
  def convert(a: String): Double = a.toDouble
}
implicit val string2int = new StringConverter[Int] {
  def convert(a: String): Int = a.toInt
}
implicit val string2long = new StringConverter[Long] {
  def convert(a: String): Long = a.toLong
}
implicit val string2bool = new StringConverter[Boolean] {
  def convert(a: String): Boolean = a.toBoolean
}

And then use them as follows:

def parameter[P](name: String)(implicit converter: StringConverter[P]): P = {
  val paramName = s"--$name"

  val res = args.sliding(2, 2).toList.collectFirst {
    case Array(`paramName`, param: String) => converter.convert(param)
  }

  res.get
}

Accepting my answer as it is (IMHO) cleaner and typesafe - it will not compile if you don't define conversion for one or more types that you use (ClassTag solution would compile and fail at runtime).

Comments, corrections, suggestions are greatly appreciated.

1 Comment

I like this one. I'm still fairly new to Scala
1

Few resources that can help you:

Example from one of the links:

scala>   val StringClass = classTag[String]
scala>   val IntClass = classTag[Int]
scala>   def typeList[T](list: List[T])(implicit tag: ClassTag[T]) =
          tag match {
            case StringClass => "It's a String!"
            case IntClass => "It's an Integer."
            case _ => "It's something else entirely"
          }

3 Comments

Thanks, for the answer. One quick question: where do you import StringClass, IntClass,... from?
First line of the code: val StringClass = classTag[String] :)
I'm blind or something:) Thanks a lot!

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.