Home > Enterprise >  Scala parsing generic argument of a function
Scala parsing generic argument of a function

Time:08-10

I want to build a function that takes as input a generic function with one argument, than parse the argument based on type and call the input function:

class PatternMatching {
    val a = "test"
    def test[T: TypeTag](callback: T => Unit): Unit = {
    callback match {
      case x if  typeOf[T] <:< typeOf[String] => callback(a)
      case x if  typeOf[T] <:< typeOf[Array[Char]] => callback(a.toCharArray)

      case _ => throw new IllegalArgumentException("error")

    }
  }
} 

I see that type is correctly inferred but is not possible to invoke the function:

type mismatch; found : PatternMatching.this.a.type (with underlying type String) required: T case x if typeOf[T] <:< typeOf[String] => callback(a)

I understand that it's expecting a type, but I can't find a way out.

CodePudding user response:

You need explicit .asInstanceOf[T] with the method you've written:

callback(a.asInstanceOf[T])

Though, what you're trying to achieve is typically done with typeclass in Scala. I let the reader search about it but the general idea is you would have your method defined like this:

def test[T](callback: T => Unit)(implicit converter: ConverterFromString[T]): Unit = {
  callback(converter.fromString(a))
}      

And there would exist in scope some values of ConverterFromString for only some types you know how to handle.

The huge benefit of this approach is to be type-safe and if will raise errors at compile time rather than runtime if a type cannot be handled.

CodePudding user response:

Using typeclass as suggested by @Gaël J works:

trait Converter[A] {
  def conveterFromString(a: String): A
}

object Converter {
  implicit val stringConverter: Converter[String] = new Converter[String] {
    def conveterFromString(x: String): String = x
  }
  implicit val arrayConverter: Converter[Array[Char]] = new Converter[Array[Char]] {
    def conveterFromString(x: String): Array[Char] = x.toCharArray
  }
}

class PatternMatching {
  val a = "test"

  def test[A](callback: A => Unit)(implicit converter: Converter[A]): Unit =
    callback(converter.conveterFromString(a))
}
  • Related