I'm beginner in scala and don't understand what happend here :
Given :
val reverse:Option[MyObject] = ...
And myObject.isNaire return Boolean.
If I do :
val v:Option[Boolean] = reverse.map(_.isNaire)
val b:Boolean = v.getOrElse(false)
It work.
Now, If I do :
val b:Boolean = reverse.map(_.isNaire).getOrElse(false)
It fail to compile with a type mismatch: found Any, required Boolean
Edit : Thanks Beryllium, by making SSCCE, I found a beginning of explication. In the first example, myObject is a java class, so isNaire is a java.lang.Boolean. I thought implicit conversion should make this transparent so the explanation is still welcome.
class Test(val naire:java.lang.Boolean)
class Other {
val testValue = Some(new Test(true))
def mysteriousCompilationError:Boolean = testValue.map(_.naire).getOrElse(false)
}
Note: ScalaCompiler is 2.10.2
In the scala.Predef there's an implicit conversion from java.lang.Boolean to scala.Boolean:
implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue
So in your first case val v:Option[Boolean] = reverse.map(_.isNaire) the compiler see's a java.lang.Boolean and looks for an implicit method in scope to convert it to a scala.Boolean, which it conveniently finds in scala.Predef.
In you're second case, testValue.map(_.naire).getOrElse(false), the compiler is doing things in this order:
Option[Test] => Option[java.lang.Boolean]
getOrElse[B >: A](default: => B): B where A is java.lang.Boolean and B is Any since scala.Boolean is not >: java.lang.Boolean
val b:Boolean, compiler can't find an implicit conversion from Any to scala.Boolean
The only way to get around this, is to tell the compiler during the map operation to use the implicit conversion from scala.Predef to go from java.lang.Boolean to scala.Boolean:
def works:Boolean = testValue.map[Boolean](_.naire).getOrElse(false)
This is a common problem and pops up often since map followed by getOrElse is very convienent. To properly fix this without the extra types, use a fold (catamorphism) over the option:
def worksToo:Boolean = testValue.fold(false)(_.naire)
By using fold you get some added type safety since there's no conversion down to common types. For instance, you can't do this:
def failsTypeCheck = testValue.fold("test")(_.naire)
While the compiler has no problem with this:
def passesTypeCheck = testValue.map(_.naire).getOrElse("test")
java.lang.Boolean and scala.Boolean is not the same. To bridge the gap you have to provide a location where the implicit conversion can do it's work.
There are some patterns to handle these types of Java/Scala interoperability problems:
If it's OK to have a different method to be used from the Scala side, you could use an implicit value class:
object Container {
implicit class Test2Scala(val test: Test) extends AnyVal {
def naireForScala: Boolean = test.naire
}
}
class Other {
val testValue = Some(new Test(true))
import Container._
def mysteriousCompilationError: Boolean =
testValue.map(_.naireForScala).getOrElse(false)
}
This does not require additional instances at run-time. It just provides another method to enrich the Java class.
If you can derive a sub class, you could preserve the method's name by using a DummyImplicit:
class Test2(_naire: Boolean) extends Test(_naire) {
def naire(implicit di: DummyImplicit): Boolean = _naire
}
class Other {
val testValue = Some(new Test2(true))
def mysteriousCompilationError: Boolean =
testValue.map(_.naire).getOrElse(false)
}
The DummyImplicit is required to get a different method signature. It's a bit tricky, requires an additional instance at run-time, but Test2 is a Test (in terms of OOP).
Wrap the Java instance in a Scala instance:
class TestWrapper(test: Test) {
def naire: Boolean = test.naire
}
class Other {
val testValue = Some(new TestWrapper(new Test(true)))
def mysteriousCompilationError: Boolean =
testValue.map(_.naire).getOrElse(false)
}
Requires an additional instance, you have to add delegates, TestWrapper is not a Test, but it's simple.
Related
I came from C++ world and new to Scala, and this behavior looks unusual.
class G1[A]( val a : A) {
//val c:A = new A //This gives compile error
def fcn1(b: A): Unit = {
//val aobj = new A // This gives compile error
println(a.getClass.getSimpleName)
println(b.getClass.getSimpleName)
}
}
def fcnWithTP[A](): Unit = {
//val a = new A // This gives compile error
//println(a.getClass.getSimpleName)
}
I am not able to crate a object using the type parameter in a class in a function body or a class body. I am only be able to use it in the function parameter.
What is the reason for this? Is this because of type erasure? At run time, the function does not know what the actual type A is, so it cannot create an object of that type?
What is the general rule for this? Does it that mean the type parameter cannot appear in function body or class definition at all? If they can actually appear, what are the examples?
Yes, you're right that this is because of erasure—you don't know anything about A at runtime that you haven't explicitly asserted about it as a constraint in the method signature.
Type erasure on the JVM is only partial, so you can do some horrible things in Scala like ask for the class of a value:
scala> List(1, 2, 3).getClass
res0: Class[_ <: List[Int]] = class scala.collection.immutable.$colon$colon
Once you get to generics, though, everything is erased, so for example you can't tell the following things apart:
scala> List(1, 2, 3).getClass == List("a", "b", "c").getClass
res1: Boolean = true
(In case it's not clear, I think type erasure is unambiguously a good thing, and that the only problem with type erasure on the JVM is that it's not more complete.)
You can write the following:
import scala.reflect.{ ClassTag, classTag }
class G1[A: ClassTag](val a: A) {
val c: A = classTag[A].runtimeClass.newInstance().asInstanceOf[A]
}
And use it like this:
scala> val stringG1: G1[String] = new G1("foo")
stringG1: G1[String] = G1#33d71170
scala> stringG1.c
res2: String = ""
This is a really bad idea, though, since it will crash at runtime for many, many type parameters:
scala> class Foo(i: Int)
defined class Foo
scala> val fooG1: G1[Foo] = new G1(new Foo(0))
java.lang.InstantiationException: Foo
at java.lang.Class.newInstance(Class.java:427)
... 43 elided
Caused by: java.lang.NoSuchMethodException: Foo.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 43 more
A better approach is to pass in the constructor:
class G1[A](val a: A)(empty: () => A) {
val c: A = empty()
}
And a much better approach is to use a type class:
trait Empty[A] {
def default: A
}
object Empty {
def instance[A](a: => A): Empty[A] = new Empty[A] {
def default: A = a
}
implicit val stringEmpty: Empty[String] = instance("")
implicit val fooEmpty: Empty[Foo] = instance(new Foo(0))
}
class G1[A: Empty](val a: A) {
val c: A = implicitly[Empty[A]].default
}
And then:
scala> val fooG1: G1[Foo] = new G1(new Foo(10101))
fooG1: G1[Foo] = G1#5a34b5bc
scala> fooG1.c
res0: Foo = Foo#571ccdd0
Here we're referring to A in the definition of G1, but we're only making reference to properties and operations that we've confirmed hold or are available at compile time.
Generics are not the same thing as templates. In C++ Foo<Bar> and Foo<Bat> are two different classes, generated at compile time.
In scala or java, Foo[T] is a single class that has with a type parameter. Consider this:
class Foo(val bar)
class Bar[T] {
val foo = new T // if this was possible ...
}
new Bar[Foo]
In C++, (an equivalent of) this would fail to compile, because there is no accessible constructor of Foo that takes no arguments. The compiler would know that when it tried to instantiate a template for Bar<Foo> class, and fail.
In scala, there is no separate class for Bar[Foo], so, at compilation time, the compiler doesn't know anything about T, other than that it is some type. It has no way of knowing whether calling a constructor (or any other method for that matter) is possible or sensible (you can't instantiate a trait for example, or an abstract class), so new T in that context has to fail: it simply does not make sense.
Roughly speaking, you can use type parameters in places where any type can be used (do declare a return type for example, or a variable), but when you are trying to do something that only works for some types, and not for others, you have to make your type param more specific. For example, this: def foo[T](t: T) = t.intValue does not work, but this: def foo[T <: Number](t: T) = t.intValue does.
Well the compiler does not know how to create an instance of type A. You need to either provide a factory function that returns instance of A, or use Manifest which creates instance of A from reflection.
With factory function:
class G1[A](val a:A)(f: () => A) {
val c:A = f()
}
With Manifest:
class G1[A](val a: A)(implicit m: scala.reflect.Manifest[A]) {
val c: A = m.erasure.newInstance.asInstanceOf[A]
}
When using type parameter, usually you will specify more details on the type A, unless you're implementing some sort of container for A that does not directly interact with A. If you need to interact with A, you need some specification on it. You can say A must be a subclass of B
class G1[A <: B](val a : A)
Now compiler would know A is a subclass of B so you can call all functions defined in B on a:A.
I have a Number Wrapper like this
class NumWrapper[A<:AnyVal](var v: A)(implicit n:Numeric[A]) {
def +(other: A): NumWrapper[A] = {
new NumWrapper(n.plus(v, other))
}
def -(other: A): NumWrapper[A] = {
new NumWrapper(n.minus(v, other))
}
}
All work fine. But when I want to have the implicit conversion, i create a companion class as followed:
object NumWrapper {
implicit def toNumWrapper[A<:AnyVal](v: A) = new NumWrapper[A](v)
}
But I have the error at compilation:
could not find implicit value for parameter n: Numeric[A]
What is wrong here ? Why it is trying to find the implicit match for type A at compilation?
Thank you very much for your help.
Implicit checks in Scala are performed at compile time (it is a statically typed language). If the compiler can't identify a single, unambiguous, matching implicit value that will be available at the calling location, it complains.
One way you can fix this here is to add the implicit requirement to the toNumWrapper method:
object NumWrapper {
implicit def toNumWrapper[A<:AnyVal](v: A)(implicit n:Numeric[A]) = new NumWrapper[A](v)
}
This pushes the requirment for an implicit Numeric out to the location where the implicit conversion is required, eg, in the console, I could then write:
scala> val chk1 = 3L
chk1: Long = 3
scala> val chk2 = NumWrapper.toNumWrapper(chk1)
chk2: NumWrapper[Long] = NumWrapper#1e89017a
And the compiler is happy because (I think - not entirely sure of this) the Long value carries its own implicit Numeric[Long] with it.
According your code,
class NumWrapper[A<:AnyVal](var v: A)(implicit n:Numeric[A])
to call new NumWrapper[MyType](v) a Numeric[MyType] must be in the scope of implicit resolution.
So when you have
object NumWrapper {
implicit def toNumWrapper[A<:AnyVal](v: A) = new NumWrapper[A](v)
}
which is calling this NumWrapper constructor, the Numeric[A] must resolved. That's not the case and the compiler raise the mentioned error.
Is it possible to write a method in Scala which returns an object of a type-parameterized class with different type paramter ? Something like this:
class A[T]
def f(switch: Boolean): A = if(switch) new A[Int] else new A[String]
Please note: The Code above is fictional to show the type of problem; The code above does not make semantically sense.
The code above will not compile because return type A is not parameterized.
You can, and you can even do it with type-safety with the aid of implicit arguments that encapsulate the pairings:
class TypeMapping[+A,B] {
def newListB = List.empty[B]
}
trait Logical
object True extends Logical
object False extends Logical
implicit val mapFalseToInt = new TypeMapping[False.type,Int]
implicit val mapTrueToString = new TypeMapping[True.type,String]
def f[A <: Logical,B](switch: A)(implicit tmap: TypeMapping[A,B]) = tmap.newListB
scala> f(True)
res2: List[String] = List()
scala> f(False)
res3: List[Int] = List()
You do have to explicitly map from boolean values to the custom True and False values.
(I have chosen List as the target class just as an example; you could pick anything or even make it generic with a little more work.)
(Edit: as oxbow_lakes points out, if you need all possible return values to be represented on the same code path, then this alone won't do it, because the superclass of List[Int] and List[String] is List[Any], which isn't much help. In that case, you should use an Either. My solution is for a single function that will be used only in the True or False contexts, and can maintain the type information there.)
One way of expressing this would be by using Either;
def f(switch: Boolean) = if (switch) Left(new A[Int]) else Right(newA[String])
This of course returns an Either[A[Int], A[String]]. You certainly cannot (at the moment) declare a method which returns some parameterized type P, with some subset of type parameters (i.e. only Int or String).
The language ceylon has union types and I understand the intention is to add these to scala in the near future, in which case, you could define a method:
def f(switch: Boolean): A[Int|String] = ...
Well, you could do something like that.
scala> class A {
| type T
| }
defined class A
scala> def f(b: Boolean): A = if(b) new A { type T = Int } else new A { type T = String }
f: (b: Boolean)A
But this is pointless. Types are a compile time information, and that information is getting lost here.
How about an absolutely minimal change to the "fictional code"? If we just add [_] after the "fictional" return type, the code will compile:
class A[T]
def f(switch: Boolean):A[_] = if(switch) new A[Int] else new A[String]
It is worth noting that A[_] is not the same as A[Any]. A[T] does not need to be defined covariant for the code to compile.
Unfortunately, information about the type gets lost.
Given a polymorphic trait like
trait Transform[T] { def apply( t: T ) : T }
one might like to implement various specialized instances, such as
case class Add[Double] extends Transform[Double] { def apply( t: Double ) ... }
case class Append[String] extends Transform[String] { def apply( t: String ) ... }
etc. Now a frequently desired transform is also the identity transform. Instead of specializing identity for each type T, it appears preferable to use just one singleton instance for all types T. My question is: what is the best way to accomplish this in Scala?
Here is what I found so far: looking at how List[T] implements List.empty[T] and Nil, I tried using Nothing as the type T. This seems to make sense, since Nothing is a subtype of every other type:
object Identity extends Transform[Nothing] {
def apply( t: Nothing ) = t
}
This seems to work. However, wherever I then want use this instance as-is, like here:
val array = Array[Transform[String]]( Transform.Identity )
I get the compiler error "type mismatch; found: Identity.type, required: Transform[String]". In order to use it, I have to explicitly cast it:
... Identity.asInstanceOf[Transform[String]]
I am not sure this is the best or even the 'proper' way to do it. Thanks for any advice.
As #Kim Stebel points out your Transform[T] is invariant in T (and has to be because T occurs in both co- and contra- variant positions in def apply(t : T) : T) so Transform[Nothing] is not a subtype of Transform[String] and can't be made to be.
If your main concern is the instance creation on each call of Kim's def Id[A] then your best model is the definition of conforms in in Predef,
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
ie. use a polymorphic method, returning a singleton value cast to the appropriate type. This is one of occasions where erasure is a win.
Applied to your situation we would have,
object SingletonId extends Transform[Any] { def apply(t : Any) = t }
def Id[A] = SingletonId.asInstanceOf[Transform[A]]
Sample REPL session,
scala> Id("foo")
res0: java.lang.String = foo
scala> Id(23)
res1: Int = 23
Since the type parameter T in Transform[T] is invariant, Transform[Nothing] is not a subtype of Transform[String], thus the compiler complains about it. But using Nothing here doesn't make sense anyway, since there can never be an instance of Nothing. So how would you pass one to the apply method? You would need to cast yet again. The only option I can see is this:
scala> def Id[A] = new Transform[A] { override def apply(t:A) = t }
Id: [A]=> java.lang.Object with Transform[A]
scala> Id(4)
res0: Int = 4
scala> Id("")
res1: java.lang.String = ""
I wrote this in scala and it won't compile:
class TestDoubleDef{
def foo(p:List[String]) = {}
def foo(p:List[Int]) = {}
}
the compiler notify:
[error] double definition:
[error] method foo:(List[String])Unit and
[error] method foo:(List[Int])Unit at line 120
[error] have same type after erasure: (List)Unit
I know JVM has no native support for generics so I understand this error.
I could write wrappers for List[String] and List[Int] but I'm lazy :)
I'm doubtful but, is there another way expressing List[String] is not the same type than List[Int]?
Thanks.
I like Michael Krämer's idea to use implicits, but I think it can be applied more directly:
case class IntList(list: List[Int])
case class StringList(list: List[String])
implicit def il(list: List[Int]) = IntList(list)
implicit def sl(list: List[String]) = StringList(list)
def foo(i: IntList) { println("Int: " + i.list)}
def foo(s: StringList) { println("String: " + s.list)}
I think this is quite readable and straightforward.
[Update]
There is another easy way which seems to work:
def foo(p: List[String]) { println("Strings") }
def foo[X: ClassTag](p: List[Int]) { println("Ints") }
def foo[X: ClassTag, Y: ClassTag](p: List[Double]) { println("Doubles") }
For every version you need an additional type parameter, so this doesn't scale, but I think for three or four versions it's fine.
[Update 2]
For exactly two methods I found another nice trick:
def foo(list: => List[Int]) = { println("Int-List " + list)}
def foo(list: List[String]) = { println("String-List " + list)}
Instead of inventing dummy implicit values, you can use the DummyImplicit defined in Predef which seems to be made exactly for that:
class TestMultipleDef {
def foo(p:List[String]) = ()
def foo(p:List[Int])(implicit d: DummyImplicit) = ()
def foo(p:List[java.util.Date])(implicit d1: DummyImplicit, d2: DummyImplicit) = ()
}
To understand Michael Krämer's solution, it's necessary to recognize that the types of the implicit parameters are unimportant. What is important is that their types are distinct.
The following code works in the same way:
class TestDoubleDef {
object dummy1 { implicit val dummy: dummy1.type = this }
object dummy2 { implicit val dummy: dummy2.type = this }
def foo(p:List[String])(implicit d: dummy1.type) = {}
def foo(p:List[Int])(implicit d: dummy2.type) = {}
}
object App extends Application {
val a = new TestDoubleDef()
a.foo(1::2::Nil)
a.foo("a"::"b"::Nil)
}
At the bytecode level, both foo methods become two-argument methods since JVM bytecode knows nothing of implicit parameters or multiple parameter lists. At the callsite, the Scala compiler selects the appropriate foo method to call (and therefore the appropriate dummy object to pass in) by looking at the type of the list being passed in (which isn't erased until later).
While it's more verbose, this approach relieves the caller of the burden of supplying the implicit arguments. In fact, it even works if the dummyN objects are private to the TestDoubleDef class.
Due to the wonders of type erasure, the type parameters of your methods' List get erased during compilation, thus reducing both methods to the same signature, which is a compiler error.
As Viktor Klang already says, the generic type will be erased by the compiler. Fortunately, there's a workaround:
class TestDoubleDef{
def foo(p:List[String])(implicit ignore: String) = {}
def foo(p:List[Int])(implicit ignore: Int) = {}
}
object App extends Application {
implicit val x = 0
implicit val y = ""
val a = new A()
a.foo(1::2::Nil)
a.foo("a"::"b"::Nil)
}
Thanks for Michid for the tip!
If I combine Daniels response and Sandor Murakozis response here I get:
#annotation.implicitNotFound(msg = "Type ${T} not supported only Int and String accepted")
sealed abstract class Acceptable[T]; object Acceptable {
implicit object IntOk extends Acceptable[Int]
implicit object StringOk extends Acceptable[String]
}
class TestDoubleDef {
def foo[A : Acceptable : Manifest](p:List[A]) = {
val m = manifest[A]
if (m equals manifest[String]) {
println("String")
} else if (m equals manifest[Int]) {
println("Int")
}
}
}
I get a typesafe(ish) variant
scala> val a = new TestDoubleDef
a: TestDoubleDef = TestDoubleDef#f3cc05f
scala> a.foo(List(1,2,3))
Int
scala> a.foo(List("test","testa"))
String
scala> a.foo(List(1L,2L,3L))
<console>:21: error: Type Long not supported only Int and String accepted
a.foo(List(1L,2L,3L))
^
scala> a.foo("test")
<console>:9: error: type mismatch;
found : java.lang.String("test")
required: List[?]
a.foo("test")
^
The logic may also be included in the type class as such (thanks to jsuereth):
#annotation.implicitNotFound(msg = "Foo does not support ${T} only Int and String accepted")
sealed trait Foo[T] { def apply(list : List[T]) : Unit }
object Foo {
implicit def stringImpl = new Foo[String] {
def apply(list : List[String]) = println("String")
}
implicit def intImpl = new Foo[Int] {
def apply(list : List[Int]) = println("Int")
}
}
def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
Which gives:
scala> #annotation.implicitNotFound(msg = "Foo does not support ${T} only Int and String accepted")
| sealed trait Foo[T] { def apply(list : List[T]) : Unit }; object Foo {
| implicit def stringImpl = new Foo[String] {
| def apply(list : List[String]) = println("String")
| }
| implicit def intImpl = new Foo[Int] {
| def apply(list : List[Int]) = println("Int")
| }
| } ; def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
defined trait Foo
defined module Foo
foo: [A](x: List[A])(implicit evidence$1: Foo[A])Unit
scala> foo(1)
<console>:8: error: type mismatch;
found : Int(1)
required: List[?]
foo(1)
^
scala> foo(List(1,2,3))
Int
scala> foo(List("a","b","c"))
String
scala> foo(List(1.0))
<console>:32: error: Foo does not support Double only Int and String accepted
foo(List(1.0))
^
Note that we have to write implicitly[Foo[A]].apply(x) since the compiler thinks that implicitly[Foo[A]](x) means that we call implicitly with parameters.
There is (at least one) another way, even if it is not too nice and not really type safe:
import scala.reflect.Manifest
object Reified {
def foo[T](p:List[T])(implicit m: Manifest[T]) = {
def stringList(l: List[String]) {
println("Strings")
}
def intList(l: List[Int]) {
println("Ints")
}
val StringClass = classOf[String]
val IntClass = classOf[Int]
m.erasure match {
case StringClass => stringList(p.asInstanceOf[List[String]])
case IntClass => intList(p.asInstanceOf[List[Int]])
case _ => error("???")
}
}
def main(args: Array[String]) {
foo(List("String"))
foo(List(1, 2, 3))
}
}
The implicit manifest paramenter can be used to "reify" the erased type and thus hack around erasure. You can learn a bit more about it in many blog posts,e.g. this one.
What happens is that the manifest param can give you back what T was before erasure. Then a simple dispatch based on T to the various real implementation does the rest.
Probably there is a nicer way to do the pattern matching, but I haven't seen it yet. What people usually do is matching on m.toString, but I think keeping classes is a bit cleaner (even if it's a bit more verbose). Unfortunately the documentation of Manifest is not too detailed, maybe it also has something that could simplify it.
A big disadvantage of it is that it's not really type safe: foo will be happy with any T, if you can't handle it you will have a problem. I guess it could be worked around with some constraints on T, but it would further complicate it.
And of course this whole stuff is also not too nice, I'm not sure if it worth doing it, especially if you are lazy ;-)
Instead of using manifests you could also use dispatchers objects implicitly imported in a similar manner. I blogged about this before manifests came up: http://michid.wordpress.com/code/implicit-double-dispatch-revisited/
This has the advantage of type safety: the overloaded method will only be callable for types which have dispatchers imported into the current scope.
Nice trick I've found from http://scala-programming-language.1934581.n4.nabble.com/disambiguation-of-double-definition-resulting-from-generic-type-erasure-td2327664.html
by Aaron Novstrup
Beating this dead horse some more...
It occurred to me that a cleaner hack is to use a unique dummy type
for each method with erased types in its signature:
object Baz {
private object dummy1 { implicit val dummy: dummy1.type = this }
private object dummy2 { implicit val dummy: dummy2.type = this }
def foo(xs: String*)(implicit e: dummy1.type) = 1
def foo(xs: Int*)(implicit e: dummy2.type) = 2
}
[...]
I tried improving on Aaron Novstrup’s and Leo’s answers to make one set of standard evidence objects importable and more terse.
final object ErasureEvidence {
class E1 private[ErasureEvidence]()
class E2 private[ErasureEvidence]()
implicit final val e1 = new E1
implicit final val e2 = new E2
}
import ErasureEvidence._
class Baz {
def foo(xs: String*)(implicit e:E1) = 1
def foo(xs: Int*)(implicit e:E2) = 2
}
But that will cause the compiler to complain that there are ambiguous choices for the implicit value when foo calls another method which requires an implicit parameter of the same type.
Thus I offer only the following which is more terse in some cases. And this improvement works with value classes (those that extend AnyVal).
final object ErasureEvidence {
class E1[T] private[ErasureEvidence]()
class E2[T] private[ErasureEvidence]()
implicit def e1[T] = new E1[T]
implicit def e2[T] = new E2[T]
}
import ErasureEvidence._
class Baz {
def foo(xs: String*)(implicit e:E1[Baz]) = 1
def foo(xs: Int*)(implicit e:E2[Baz]) = 2
}
If the containing type name is rather long, declare an inner trait to make it more terse.
class Supercalifragilisticexpialidocious[A,B,C,D,E,F,G,H,I,J,K,L,M] {
private trait E
def foo(xs: String*)(implicit e:E1[E]) = 1
def foo(xs: Int*)(implicit e:E2[E]) = 2
}
However, value classes do not allow inner traits, classes, nor objects. Thus also note Aaron Novstrup’s and Leo’s answers do not work with a value classes.
I didn't test this, but why wouldn't an upper bound work?
def foo[T <: String](s: List[T]) { println("Strings: " + s) }
def foo[T <: Int](i: List[T]) { println("Ints: " + i) }
Does the erasure translation to change from foo( List[Any] s ) twice, to foo( List[String] s ) and foo( List[Int] i ):
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ108
I think I read that in version 2.8, the upper bounds are now encoded that way, instead of always an Any.
To overload on covariant types, use an invariant bound (is there such a syntax in Scala?...ah I think there isn't, but take the following as conceptual addendum to the main solution above):
def foo[T : String](s: List[T]) { println("Strings: " + s) }
def foo[T : String2](s: List[T]) { println("String2s: " + s) }
then I presume the implicit casting is eliminated in the erased version of the code.
UPDATE: The problem is that JVM erases more type information on method signatures than is "necessary". I provided a link. It erases type variables from type constructors, even the concrete bound of those type variables. There is a conceptual distinction, because there is no conceptual non-reified advantage to erasing the function's type bound, as it is known at compile-time and does not vary with any instance of the generic, and it is necessary for callers to not call the function with types that do not conform to the type bound, so how can the JVM enforce the type bound if it is erased? Well one link says the type bound is retained in metadata which compilers are supposed to access. And this explains why using type bounds doesn't enable overloading. It also means that JVM is a wide open security hole since type bounded methods can be called without type bounds (yikes!), so excuse me for assuming the JVM designers wouldn't do such an insecure thing.
At the time I wrote this, I didn't understand that stackoverflow was a system of rating people by quality of answers like some competition over reputation. I thought it was a place to share information. At the time I wrote this, I was comparing reified and non-reified from a conceptual level (comparing many different languages), and so in my mind it didn't make any sense to erase the type bound.