Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala polymorphism for type casting

Tags:

scala

I have some methods like:

  def floatValueOrNone(value: String): Option[Float] = {
    if (!value.equals("NA")) Option(value.toFloat) else None
  }

  def intValueOrNone(value: String): Option[Int] = {
    if (!value.equals("NA")) Option(value.toInt) else None
  }

  def stringValueOrNone(value: String): Option[String] = {
    if (!value.equals("NA")) Option(value.toString) else None
  }

I'd like to use one method for all of these, but types can't be stored in a variable so I was wondering if there is a small clean way to do this.

like image 926
Sahil Sareen Avatar asked Nov 21 '25 11:11

Sahil Sareen


2 Answers

I'd recommend not using implicit conversions here. Implicit conversions make your code more difficult to reason about and maintain, slow down compilation time (this isn't often a concern, but when it is, it can be really bad), and require a special feature flag in recent Scala versions.

Instead you can use a custom type class, which I'll call Read:

trait Read[A] { def apply(s: String): Option[A] }

object Read {
  def instance[A](parse: String => A): Read[A] = new Read[A] {
    def apply(s: String): Option[A] = if (s != "NA") Option(parse(s)) else None
  }

  implicit val floatRead: Read[Float] = instance(_.toFloat)
  implicit val intRead: Read[Int] = instance(_.toInt)
  implicit val stringRead: Read[String] = instance(identity)

  def valueOrNone[A](s: String)(implicit read: Read[A]): Option[A] = read(s)
}

And then:

scala> Read.valueOrNone[Int]("100")
res0: Option[Int] = Some(100)

scala> Read.valueOrNone[String]("100")
res1: Option[String] = Some(100)

This is very similar to requiring an implicit conversion implicitly, but it has the advantage of not requiring you to pollute your implicit scope with conversions that could be applied in places you don't expect.

like image 104
Travis Brown Avatar answered Nov 23 '25 17:11

Travis Brown


Implicit conversions to the rescue:

def valueOrNone[T](value: String)(implicit conv: String => T): Option[T] = {
  if (!value.equals("NA")) Option(conv(value)) else None
}

This will work for all cases you stated. If you want to convert to some non-standard type that doesn't have a default implicit conversion in scope, then make sure to provide one:

class MyCustomClass(str: String) { 
  override def toString = "my custom class: " + str
}

implicit def fromString(str: String) = new MyCustomClass(str)

println(valueOrNone[MyCustomClass]("foo")) // prints "my custom class: foo"
like image 25
slouc Avatar answered Nov 23 '25 16:11

slouc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!