views:

682

answers:

4

I am trying to create an implicit conversion from any type (say, Int) to a String...

An implicit conversion to String means RichString methods (like reverse) are not available.

implicit def intToString(i: Int) = String.valueOf(i)
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => error: value reverse is not a member of Int
100.length // => 3

An implicit conversion to RichString means String methods (like toCharArray) are not available

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.reverse // => "001"
100.toCharArray  // => error: value toCharArray is not a member of Int
100.length // => 3

Using both implicit conversions means duplicated methods (like length) are ambiguous.

implicit def intToString(i: Int) = String.valueOf(i)
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i))
100.toCharArray  // => Array[Char] = Array(1, 0, 0)
100.reverse // => "001"
100.length // => both method intToString in object $iw of type 
   // (Int)java.lang.String and method intToRichString in object
   // $iw of type (Int)scala.runtime.RichString are possible 
   // conversion functions from Int to ?{val length: ?}

So, is it possible to implicitly convert to String and still support all String and RichString methods?

+2  A: 

The only option I see is to create a new String Wrapper class MyString and let that call whatever method you want to be called in the ambiguous case. Then you could define implicit conversions to MyString and two implicit conversions from MyString to String and RichString, just in case you need to pass it to a library function.

Kim
This sounds like a fair approach, but a lot of work. Plus I suspect it is not future proof, as other implicit conversions are introduced.
Synesso
Sure it is a lot of work, but not a lot of thinking, just typing. ;) And why wouldn't it be future proof? Nobody forces you to use any implicit conversions that might be introduced in the future, not even those in the preload.
Kim
+1  A: 

I'm confused: can't you use .toString on any type anyway thus avoiding the need for implicit conversions?

oxbow_lakes
I don't think that would change matters, except to perhaps introduce null pointer exceptions.To be clear, you are proposing changing String.valueOf(i) to i.toString ?
Synesso
+2  A: 

Either make a huge proxy class, or suck it up and require the client to disambiguate it:

100.asInstanceOf[String].length

Mitch Blevins
It seems that 'suck it up' is the best option. API design in Scala is significantly more difficult than being an API consumer. I wanted to create a datatype that wraps a value and allows the API user to treat the datatype as if it were the type of the interned value. E.g. if it wraps an Int, then treat the object like an int. Turns out this is too difficult.I'm considering giving my datatype helper methods:type Tval internedValue: Tdef s = internedValue.asInstanceOf[String]def i = internedValue.asInstanceOf[Int]... etc
Synesso
Prefer `(100: String).length`, as this is type-safe, whereas `asInstanceOf` isn't. Besides, it's prettier and shorter.
Daniel
+1  A: 

I don't have a solution, but will comment that the reason RichString methods are not available after your intToString implicit is that Scala does not chain implicit calls (see 21.2 "Rules for implicits" in Programming in Scala).

If you introduce an intermediate String, Scala will make the implict converstion to a RichString (that implicit is defined in Predef.scala).

E.g.,

$ scala
Welcome to Scala version 2.7.5.final [...].
Type in expressions to have them evaluated.
Type :help for more information.

scala> implicit def intToString(i: Int) = String.valueOf(i)
intToString: (Int)java.lang.String

scala> val i = 100
i: Int = 100

scala> val s: String = i
s: String = 100

scala> s.reverse
res1: scala.runtime.RichString = 001
Richard Dallaway
That is good to know! Thank you.
Synesso