Say that, for aesthetical reasons, I want to be able to write:
3 / 4
and have / be a method on a class that there exists an implicit conversion from Int to, e.g.:
class Foo(val i: Int) {
def /(that: Int) = // something
}
implicit def intToFoo(i: Int) = new Foo(i)
Is this at all possible, i.e. is it possible to "disable" the / method on Int?
In short: No, you can't.
Implicit resolution will only take place if you attempt to call a method that doesn't already exist.
A more "idiomatic" solution would be to create your own pseudo-number type, something like:
case class Rational(a: Int, b: Int) {
// other methods
}
val foo = Rational(3, 4)
or
case class Path(value: String) {
def /(other: String): Path = ...
}
val p = Path("3") / "4"
Is there a reason that something like
trait PathElement[T] { val value: T }
case class IntElement(value: Int) extends PathElement[Int]
case class StringElement(value: String) extends PathElement[String]
case class Path(parts: Seq[PathElement[_]]) {
def /(other: Path): Path = copy(parts = parts ++ other.parts)
}
object Path {
def apply(part: PathElement[_]): Path = Path(List(part))
implicit def int2path(i: Int): Path = Path(IntElement(i))
implicit def str2path(s: String): Path = Path(StringElement(s))
}
wouldn't work for you? This would allow you to write, for example,
import Path._
"foo" / 3 / 4 / "bar"
This works because String does not have its own / method, so the first "foo" is implicitly converted to a Path. If you were starting a Path with an Int, you'd have to convert it explicitly, but you'd get any other Ints for free.
I can of course only guess what you really want to accomplish but I assume you don’t just want to match concrete URLs but also extract information from given strings. E.g. when given "/foo/21" you don’t just want to know that this matches some "foo" / 21 but you want to do something with the value of 21.
I’ve found the URI matching process in Lift to be quite useful, so maybe that fits your use case. (I’m using a very simplified version, of course.) It’s done with Lists there which makes matching a little easier but it also means you’ll have to use :: instead of /.
But that’s not the point: what I want to show is the advantage of not using implicit conversions and the power of extractors
object AsInt {
def unapply(i: String): Option[Int] = try {
Some(i.toInt)
} catch {
case e: java.lang.NumberFormatException => None
}
}
def matchUrl(url: String) = {
val req:List[String] = url.split('/').toList.drop(1)
req match {
case "foo" :: "bar" :: Nil => println("bar")
case "foo" :: AsInt(i) :: Nil => println("The square is " + i*i)
case "foo" :: s :: Nil => println("No int")
case _ => println("fail")
}
}
matchUrl("/foo/21")
matchUrl("/foo/b")
matchUrl("/foo/bar")
matchUrl("/foobar")
// prints:
// The square is 441
// No int
// bar
// fail
In short, using the AsInt extractor instead of an implicit conversion of Int to String you can actually retrieve the integer value from the string if and only if it is convertible and of course use it immediately. Obviously, if you don’t like the naming, you can change it to something more unobtrusive but if you really want to do url matching, you maybe should not convert everything implicitly.
Related
I have a case class like the following except it has far more parameters (all optional):
case class MyClass(i: Option[String], j: Option[Int], n: Option[OtherClass])
I want to loop over the parameters and verify that only i and j are defined, while the rest are None. If the case class changes in the future I want to keep the same validation in place, which is why I am not using pattern matching or explicitly checking.
What is a good a way of doing this in scala 2.12?
You could use productIterator as #Tim explains, however this smells kinda bad. If there is a special situation in which only MyClass(Some(_), Some(_), None) is valid, but MyClass(Some(_), Some(_), Some(_)) is not, it appears that these should be two distinct types rather than a "catchall" class with ad-hoc validation tackled on the side.
Consider something like this:
sealed trait Foo {
def i: Option[String]
def j: Option[Int]
def n: Option[OtherClass] = None
}
case class IJ(iVal: String, jVal: Int) extends Foo {
def i = Some(iVal)
def j = Some(jVal)
}
case class IJN(i: Option[String], j: Option[Int], nVal: OtherClass) extends Foo {
override def n = Some(nVal)
}
This way, you can simply check the type of the object you are dealing with to distinguish between the two situations:
foo match {
case x: IJ => "i and j are defined, but n is not"
case x: IJN => "N is defined, but i and j might not be"
}
You can do this using productIterator which is a list of fields for the case class:
def verify(mc: MyClass) = {
val c = mc.productIterator.count{
case o: Option[_] => o.nonEmpty
case _ => true
}
mc.i.nonEmpty && mc.j.nonEmpty && c == 2
}
However this kind of generic code has risks, so I would recommend avoiding a case class with a lot of fields. Consider grouping various fields into their own object and the making the test explicit on the known fields.
Updating the test when the number of fields changes is not a great chore, and is an opportunity to check that those changes have not affected your code.
Original version using method:
case class MyClass(i: Option[String], j: Option[Int], n: Option[OtherClass])
{
def verify = {
val c = this.productIterator.count{
case o: Option[_] => o.nonEmpty
case _ => true
}
i.nonEmpty && j.nonEmpty && c == 2
}
}
I figured out you can do this through reflection, although it may not be recommended see note below:
val allowableFieldNames = Set("i", "j")
val fields = universe.typeOf[MyClass].decls.collect {
case m: MethodSymbol if m.isCaseAccessor => m.name.toString
}.toList
val values = myClass.productIterator.toList
values.zip(fields).collect {
case(o: Option[_], field: String) if !allowableFieldNames(field) => assert(o.isEmpty)
}
This is assuming that productIterator and universe.typeOf[].decls return the parameters and their values in the same order.
From #Dima:
Reflection is a kind of a "meta-tool" you resort to (generally, when creating low-level libraries) for a specific reason, to address a specific issue that scala standard toolset does not provide for.
When I try to run following code:
def config[T](key: String): Option[T] = {
//in reality this is a map of various instance types as values
Some("string".asInstanceOf[T])
}
config("path").orNull
I'm getting error:
java.lang.String cannot be cast to scala.runtime.Null$
java.lang.ClassCastException
Following attempts are working fine:
config[String]("path").orNull
config("path").getOrElse("")
Since getOrElse works its confusing why null is so special and throws an error. Is there a way for orNull to work without specifying generic type ?
scalaVersion := "2.12.8"
Just to show how you may avoid the use asInstanceOf to get the values from a typed config.
sealed trait Value extends Product with Serializable
final case class IntValue(value: Int) extends Value
final case class StringValue(value: String) extends Value
final case class BooleanValue(value: Boolean) extends Value
type Config = Map[String, Value]
sealed trait ValueExtractor[T] {
def extract(config: Config)(fieldName: String): Option[T]
}
object ValueExtractor {
implicit final val IntExtractor: ValueExtractor[Int] =
new ValueExtractor[Int] {
override def extract(config: Config)(fieldName: String): Option[Int] =
config.get(fieldName).collect {
case IntValue(value) => value
}
}
implicit final val StringExtractor: ValueExtractor[String] =
new ValueExtractor[String] {
override def extract(config: Config)(fieldName: String): Option[String] =
config.get(fieldName).collect {
case StringValue(value) => value
}
}
implicit final val BooleanExtractor: ValueExtractor[Boolean] =
new ValueExtractor[Boolean] {
override def extract(config: Config)(fieldName: String): Option[Boolean] =
config.get(fieldName).collect {
case BooleanValue(value) => value
}
}
}
implicit class ConfigOps(val config: Config) extends AnyVal {
def getAs[T](fieldName: String)(default: => T)
(implicit extractor: ValueExtractor[T]): T =
extractor.extract(config)(fieldName).getOrElse(default)
}
Then, you can use it like this.
val config = Map("a" -> IntValue(10), "b" -> StringValue("Hey"), "d" -> BooleanValue(true))
config.getAs[Int](fieldName = "a")(default = 0) // res: Int = 10
config.getAs[Int](fieldName = "b")(default = 0) // res: Int = 0
config.getAs[Boolean](fieldName = "c")(default = false) // res: Boolean = false
Now, the problem becomes how to create the typed config from a raw source.
And even better, how to directly map the config to a case class.
But, those are more complex, and probably is better to just use something already done, like pureconfig.
Just as an academic exercise, lets see if we can support Lists & Maps.
Lets start with lists, a naive approach would be to have another case class for values which are lists, and create a factory of extractors for every kind of list (this process is formally know as implicit derivation).
import scala.reflect.ClassTag
final case class ListValue[T](value: List[T]) extends Value
...
// Note that, it has to be a def, since it is not only one implicit.
// But, rather a factory of implicits.
// Also note that, it needs another implicit parameter to construct the specific implicit.
// In this case, it needs a ClasTag for the inner type of the list to extract.
implicit final def listExtractor[T: ClassTag]: ValueExtractor[List[T]] =
new ValueExtractor[List[T]] {
override def extract(config: Config)(fieldName: String): Option[List[T]] =
config.get(fieldName).collect {
case ListValue(value) => value.collect {
// This works as a safe caster, which will remove all value that couldn't been casted.
case t: T => t
}
}
}
Now, you can use it like this.
val config = Map("l" ->ListValue(List(1, 2, 3)))
config.getAs[List[Int]](fieldName = "l")(default = List.empty)
// res: List[Int] = List(1, 2, 3)
config.getAs[List[String]](fieldName = "l")(default = List("Hey"))
// res: String = List() - The default is not used, since the field is a List...
// whose no element could be casted to String.
However, this approach is limited to plain types, if you need a List of other generic type, like a List of Lists. Then, this won't work.
val config = Map("l" ->ListValue(List(List(1, 2), List(3))))
val l = config.getAs[List[List[String]]](fieldName = "l")(default = List.empty)
// l: List[List[String]] = List(List(1, 2), List(3)) ???!!!
l.head
// res: List[String] = List(1, 2)
l.head.head
// java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
The problem here is type erasure, which ClassTags can not solve, you may try to use TypeTags which can preserve the complete type, but the solution becomes more cumbersome.
For Maps the solution is quite similar, especially if you fix the key type to String (assuming what you really want is a nested config). But, this post is too long now, so I would leave it as an exercise for the reader.
Nevertheless, as already said, this can be broken easily, and is not completely robust.
There are better approaches, but I myself am not very skilled on those (yet), and even if I would be, the answer would be more long and really not necessary at all.
Lucky for you, even if pureconfig does not support YAML directly, there is a module which does, pureconfig-yaml.
I would suggest you to take a look to the module, and if you have further problems ask a new question tagging pureconfig and yaml directly. Also, if it is just a small doubt, you may try asking in thegitter channel.
Curious if anyone has a creative approach for the following:
def toDouble(any: Any): Option[Double] = // { if any has a .toDouble method call it else return None }
For instance, Int, String, Long all have a .toDouble method. I'd like it to be called if it exist (even non primitive types).
Ideally something like this (without .toString'ing everything first)
def toDouble(any: Any): Option[Double] = {
case any.hasToDouble => Try(any.toDouble).toOption
case _ => None
}
You can use
def doubleFromAny(in: Any): Option[Double] = {
Try{in.asInstanceOf[{def toDouble: Double}].toDouble}.toOption
}
The problem with this is any toDouble provided through an implicit won't work (so the string "4.5" would give you None). I also expect performance would not be great.
In the end, you need to figure out what types are possible and use something like Jon Anderson's answer to check and cast each type.
You can use pattern matching. This has the additional benefit of giving you more explicit control over how the conversion is done. (Eg, if you wanted to try additional string parsing)
def toDouble(any: Any): Option[Double] = {
any match {
case n:Int => Some(n.toDouble)
case n:Long => Some(n.toDouble)
case n:Double => Some(n.toDouble)
case s:String => Try(s.toDouble).toOption
case _ => None
}
}
def toDouble(a: Any): Option[Double] = try {
if (a == null) None else {
Some(a.toString.toDouble)
}
} catch {
case scala.util.control.NonFatal(e) => None
}
Others have suggested good answers. Another way I thought of doing was using implicit object. For your above example you could write something like:
sealed trait ImplicitType[T]
object ImplicitType{
implicit object IntType extends ImplicitType[Int]
implicit object StringType extends ImplicitType[String]
implicit object LongType extends ImplicitType[Long]
implicit object DoubleType extends ImplicitType[Double]
}
def toDouble[T : ImplicitType](n: T) : Option[Double] = {
Try(n.toString.toDouble).toOption
}
The above works because, the compiler constraint is from the availability of implicit objects in ImplicitType companion object. The context bound implicit def f: ImplicitType[T] by default searches for implicit objects inside companion object of ImplicitType. So now you can do things like:
val foo = toDouble(1) // Some(1.0)
val foo1 = toDouble("2") //Some(2.0)
val foo2 = toDouble(1L) //Some(1.0)
val foo3 = toDouble("s") //None
val foo4 = toDouble(1.23456e300d) //Some(1.23456E300)
In this way your toDouble function does not change at all. I couldn't think of a way to avoid toString sorry. Hope this works for you.
I want to use a Scala extractor to match my custom type with a String (in that specific order, not String to Scala). Here is my code:
class Scala
object Scala {
def apply(s: String) = new Scala
def unapply(sc: Scala) = Some("Scala")
}
class ExtendedString(s: String) {
def unapply(sc: Scala): Option[String] = {
Some(s)
}
}
implicit def str2Scala(s: String): ExtendedString = {
new ExtendedString(s)
}
Scala match {
case "abc" => println("Aha!")
case "def" => println("Yeah!")
case "scala" => println("Scala!")
}
But it does not work. I get an error:
Error:(20, 9) type mismatch;
found : String("abc")
required: A$A109.this.Scala.type
case "abc" => println("Aha!")
^
How can I fix it to make extractor work?
PS
My original idea was to provide an implicit converter to extend String class with ExtendedString class and then to implement unapply method there to make extraction possible.
There should be no need whatsoever for implicits if all you want is an extractor.
To define an extractor, you make an object with an unapply method. E.g.
object MyExtractor {
def unapply(value: ValueBeingMatched): Option[ExtractedValue] = { ... }
}
Then you match a value using the extractor
val myValue: ValueBeingMatched = ...
myValue match {
case MyExtractor(extractedValue) => println(s"I got $extractedValue!")
}
If you want your extractor to come back with multiple values, the unapply should return an option of tuple:
object MyExtractor {
def unapply(value: ValueBeingMatched): Option[(ResultType1, ResultType2, ...)] = { ... }
}
val myValue: ValueBeingMatched = ...
myValue match {
case MyExtractor(result1, result2, ...) => ...
}
I'm not clear on what you are trying to accomplish from your example code, so I'll make an example that maybe is relevant for you.
Let's say you make a custom Point class:
case class Point(x: Int, y: Int)
And you want to be able to extract points from a String. In this case, the ValueBeingMatched is a String, and the ExtractedValue is a Point. I'll also define the extractor in the Point object. As for functionality, let's assume that a string like "12,8" corresponds to a Point(12, 8).
object Point {
def unapply(s: String): Option[Point] = {
val parts = s.split(",")
// this is just example code, so I won't handle failures,
// but if it did fail, you'd return a None instead of Some
Some(Point(parts(0).toInt, parts(1).toInt))
}
}
Now that it's defined, you can match strings using the Point extractor:
val s = "3,4"
s match {
case Point(p) => // p is actually an instance of Point
}
edit to respond to feedback:
In order to match directly to a string, the value being matched must already be a String. So one way would be to add a converter method e.g.
instanceOfMyType.convertToString match {
case "abc" => println("Aha!")
}
Or you would have to write an extractor to allow
instanceOfMyType match {
case Extracted("abc") => println("Aha!")
}
I keep coming across situations where I want to use extractors as parsers, they are really useful for this but it never looks right, often the unapply looks like it should be an apply. Also, the parsing extractor may get in the way of the default extractor. Are there any patterns for this?
case class AnID(int: Int) extends AnyVal
object AnID {
def unapply(idString: String): Option[AnID] = "id-([0-9]+)".r match {
case Seq(intString: String) => Try(Integer.parseInt(intString)).map(AnID.apply).toOption
case _ => None
}
}
And a test:
AnID(8) should be (AnID(8))
val AnID(id1) = "id-1"
id1 should be (AnID(1))
val AnID(id2) = AnID(2)
id2 should be (2)
It is kind of strange.
You can create a Parse object inside the companion object to make it more clear that you're parsing.
case class AnID(int: Int) extends AnyVal
object AnID {
private val Reg = "id-([0-9]+)".r
object Parse {
def unapply(s: String): Option[AnID] = s match {
case Reg(digits) => Some(new AnID(digits.toInt))
case _ => None
}
}
}
So now you at least don't clobber your normal extractor and have things that look like val AnID.Parse(id) = "id-9".
Maybe that won't solve your general unease with pattern matching looking kind of backwards. If not, you can always make Parse just implement an apply, and then you get val Option(x) = AnID.Parse("id-9").