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 Int
s 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With