In the purpose of learning, I would like to understand how "::" class is working.
So I decided to create my own but instead of calling it "::" I want to call it ":++:"
From List.scala source code, the University of Helsinki and Programming In Scala 2nd edition (Chapter 22) I should write:
abstract class List[+A] {
def isEmpty: Boolean
def head: A
def tail: List[A]
}
final case class :++:[A](head: A, tail: List[A]) extends List[A] {
override def isEmpty: Boolean = false
}
I can create "::" objects
new ::(1, List(2))
But I can't create my ":++:" objects:
new :++:(1, List(2))
<console>:12: error: type mismatch;
found : List[Int]
required: List[?]
new :++:(1, List(2))
Where is my mistake?
Annotating the :++: type parameter gives you a hint:
scala> new :++:[Int](1, List(2))
<console>:11: error: type mismatch;
found : scala.collection.immutable.scala.collection.immutable.List[Int]
required: List(in object $iw)[Int]
new :++:[Int](1, List(2))
^
The :++: constructor expects one of your custom List[A] instances, but you give it a normal Scala List(1).
But you currently have no way to create an instance of your list (other than having a null tail). If you add an equivalent of Nil, then you are all good:
case object Empty extends List[Nothing] {
def head = ???
def isEmpty = true
def tail = ???
}
And then you can create a :++::
scala> val a = new :++:[Int](1, Empty)
a: :++:[Int] = :++:(1,Empty)
scala> val b = new :++:(2, a)
b: :++:[Int] = :++:(2,:++:(1,Empty))
In addition to gourlaysama's answer which explains why your definition is shadowed by the built-in List, I'd like to drop a few hints.
Per answer above, you should also add the Empty or CNil case object for the 'empty' list.
If you check the sources again, you'll find 'cons' method in the built-in List, similar to one below:
sealed abstract class ConsList[+A] {
(...)
def :++:[B >: A](x : B) : ConsList[B] = new :++:(x, this)
}
It allows you to create the list using infix notation:
val b = 2 :++: 4 :++: 7 :++: CNil
And finally, you can create a companion object for easier list creation:
object ConsList {
def apply[A](xs : A*) = {
xs.foldRight(CNil : ConsList[A])((el, l) => el :++: l)
}
}
Which means that you can now create val c = ConsList(2, 4, 7) which will be equivalent to b above, and also equivalent to :++:(2, :++:(4, :++:(7, CNil)))
Related
This question already has an answer here:
Creating an object from a type parameter in Scala
(1 answer)
Closed 2 years ago.
How can one construct an object from a type alias in scala?
type MyType = List[Int]
println(List[Int]())
println(MyType()) // error: not found: value MyType
This is problematic in a function that must return a new instance of that type. Basic Example:
def foo(x: MyType): MyType = {
if (x.head == 0) MyType() // Should Nil be used?
else if (x.head == -1) new MyType(1,2,3,4)
else x
}
How can foo become unaware of the actual type of MyType?
Scala (like Java) has different namespaces for types and values, and a type alias only introduces the alias into the type namespace. In some cases you can pair the alias with a val referring to the companion object to get the effect you're looking for:
scala> case class Foo(i: Int)
defined class Foo
scala> type MyType = Foo
defined type alias MyType
scala> val MyType = Foo
MyType: Foo.type = Foo
scala> MyType(1)
res0: Foo = Foo(1)
This won't work with List[Int], though, since while both the List type and the List companion object's apply method have a type parameter, the List companion object itself doesn't.
Your best bet is to use something like Nil: MyType, but you'll find that in general using type aliases like this (i.e. just as a kind of abbreviation) often isn't the best solution.
A type is just a type, not any information about how to create instances of that.
You would have to provide it with a function that constructs an instance of the alias.
So for example
def foo(x: MyType)(create: List[Int] => MyType) =
if (x.head == 0) create(Nil) // Should Nil be used?
else if (x.head == -1) create(List(1,2,3,4))
else x
But as Erik says, it sounds like you are doing something a bit backwards.
It looks to me you're trying to achieve something with the wrong set of tools. But in any case there is no way to instantiate a type alias.
Is your goal to make the function work on a set of different collection types as opposed to only Lists? In that case you need more than just nominal decoupling; currently the function still relies on the method signatures of List.
You can decouple your function from the interface of List by using type classes, but you'll have to wrap every method call you'll ever want to make in MyOps:
import scala.language.higherKinds
import scala.reflect.ClassTag
trait MyOps[L[_], T] {
def head(xs: L[T]): T
def tail(xs: L[T]): L[T]
def fromList(xs: List[T])(implicit ev: ClassTag[T]): L[T]
}
implicit def listMyOps[T] = new MyOps[List, T] {
def head(xs: List[T]) = xs.head
def tail(xs: List[T]) = xs.tail
def fromList(xs: List[T])(implicit ev: ClassTag[T]) = xs
}
implicit def arrayMyOps[T] = new MyOps[Array, T] {
def head(xs: Array[T]) = xs(0)
def tail(xs: Array[T]) = xs.slice(1, xs.size)
def fromList(xs: List[T])(implicit ev: ClassTag[T]) = xs.toArray
}
def foo[L[_]](xs: L[Int])(implicit ev: MyOps[L, Int]) = {
ev.fromList(xs = if (ev.head(xs) == -1) List(1, 2, 3) else Nil)
}
println(foo(List(0, 1, 2, 6)))
println(foo(Array(-1, 6, 8)))
outputs:
List()
[I#54b63af0
— the first foo call takes a List and returns a List; the 2nd one Array and Array respectively.
In the world of Scala Collections for every collection type there exists a companion object that helps make it easier to construct instances of the collection.
For simplicity consider this class and object of the same name:
object Numeric {
def apply(s: String): Numeric = new Numeric(s.toDouble)
}
case class Numeric(v: Double)
If you only had the case class then writing Numeric("5.1") would be erroneous. With the object you can actually now call Numeric.apply("5.1") or (because apply is a special method) you can simply write Numeric("5.1"). object in Scala is analogous to holding all static methods that you would write in Java.
Back to your example, MyType is only the type alias to List[Int] and does not bring the List companion object into scope with the name MyType. Your example is the equivalent of my Numeric example without the companion object.
Thus, my answer, is to create a simple and generic way to construct your companion object that hides the fact that 1) MyType is an alias and 2) that it restricts the collection type to Int. If you have lots of type aliases like this one in your code then you'll probably want the more generic version here:
import scala.collection.GenTraversable
import scala.collection.generic.GenericCompanion
class TypeCompanion[CC[X] <: GenTraversable[X], T](companion: GenericCompanion[CC]) {
type InnerType = CC[T]
def apply(elems: T*): InnerType = companion.apply(elems: _*)
def empty(): InnerType = companion.empty[T]
}
object MyType extends TypeCompanion[List, Int](List)
type MyType = MyType.InnerType
If you want to reduce the number of times that you write List, and will not mind the extra typing if you need to change from Int to some other type, then you may prefer this variation:
class TypeCompanion[CC[X] <: GenTraversable[X]](companion: GenericCompanion[CC]) {
type InnerType = CC[Int]
def apply(elems: Int*): InnerType = companion.apply(elems: _*)
...
}
object MyType extends TypeCompanion(List)
type MyType = MyType.InnerType
Both implementations give your foo method this implementation:
def foo(x: MyType): MyType = {
if (x.head == 0) MyType()
else if (x.head == -1) MyType(1,2,3,4)
else x
}
Note that the type restrictions on GenTraversable and GenericCompanion are just a clever way of restricting to companion objects that match scala collection's conventions.
I've created the following class:
class Foo[T] extends collection.mutable.HashMap[T, Int] {...}
(The class has some methods, but to reproduce this problem it doesn't need any.)
When I clone an instance of this class and try to use it as another instance of Foo, I get the following type error:
scala> val f = new Foo[String]
f: Foo[String] = Map()
scala> val anotherF: Foo[String] = f.clone
<console>:9: error: type mismatch;
found : scala.collection.mutable.HashMap[String,Int]
required: Foo[String]
How do I cast the result of f.clone as an instance of Foo[String] ?
Update A commenter pointed out that I could override the clone method on HashMap. No objections to doing so, but it seems like I should be able to do so without reimplementing the actual cloning process.
The HashMap class returns HashMap objects, whether it is extended or not. If you want to change the return value, you have to extend MapLike as well.
Following the comment from Rex Kerr, the approach presented in the question is just wrong.
Instead, the scala-idiomatic approach is to use implicit definitions to add the increment method to HashMap objects:
type Counter[T] = collection.mutable.HashMap[T, Int]
class WrappedHash[T](val h: collection.mutable.HashMap[T, Int]) {
def increment(key: T) = {
val currentKeyCount: Int = h.getOrElse(key, 0)
h ++= Map(key -> (currentKeyCount + 1))
}
}
implicit def wrapHash[T](h: collection.mutable.HashMap[T, Int]) = new WrappedHash[T](h)
I see this code in this blog: Type-Level Programming in Scala:
// define the abstract types and bounds
trait Recurse {
type Next <: Recurse
// this is the recursive function definition
type X[R <: Recurse] <: Int
}
// implementation
trait RecurseA extends Recurse {
type Next = RecurseA
// this is the implementation
type X[R <: Recurse] = R#X[R#Next]
}
object Recurse {
// infinite loop
type C = RecurseA#X[RecurseA]
}
There is an operator # in the code R#X[R#Next] which I've never seen. Since it's difficult to search it(ignored by search engines), who can tell me what does it mean?
To explain it, we first have to explain nested classes in Scala. Consider this simple example:
class A {
class B
def f(b: B) = println("Got my B!")
}
Now let's try something with it:
scala> val a1 = new A
a1: A = A#2fa8ecf4
scala> val a2 = new A
a2: A = A#4bed4c8
scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
found : a1.B
required: a2.B
a2.f(new a1.B)
^
When you declare a class inside another class in Scala, you are saying that each instance of that class has such a subclass. In other words, there's no A.B class, but there are a1.B and a2.B classes, and they are different classes, as the error message is telling us above.
If you did not understand that, look up path dependent types.
Now, # makes it possible for you to refer to such nested classes without restricting it to a particular instance. In other words, there's no A.B, but there's A#B, which means a B nested class of any instance of A.
We can see this in work by changing the code above:
class A {
class B
def f(b: B) = println("Got my B!")
def g(b: A#B) = println("Got a B.")
}
And trying it out:
scala> val a1 = new A
a1: A = A#1497b7b1
scala> val a2 = new A
a2: A = A#2607c28c
scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
found : a1.B
required: a2.B
a2.f(new a1.B)
^
scala> a2.g(new a1.B)
Got a B.
It's known as type projection, and is used to access type members.
scala> trait R {
| type A = Int
| }
defined trait R
scala> val x = null.asInstanceOf[R#A]
x: Int = 0
Basically, it's a way of referring to classes within other classes.
http://jim-mcbeath.blogspot.com/2008/09/scala-syntax-primer.html (search for "pound")
Here's a resource for searching on "symbolic operators" (which are really methods), but I haven't figured out how to escape "#" to search on in scalex)
http://www.artima.com/pins1ed/book-index.html#indexanchor
I have seen a function named implicitly used in Scala examples. What is it, and how is it used?
Example here:
scala> 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>:8: error: could not find implicit value for evidence parameter of type
Foo[Double]
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.
Also see How to investigate objects/types/etc. from Scala REPL? and Where does Scala look for implicits?
implicitly is avaliable in Scala 2.8 and is defined in Predef as:
def implicitly[T](implicit e: T): T = e
It is commonly used to check if an implicit value of type T is available and return it if such is the case.
Simple example from retronym's presentation:
scala> implicit val a = "test" // define an implicit value of type String
a: java.lang.String = test
scala> val b = implicitly[String] // search for an implicit value of type String and assign it to b
b: String = test
scala> val c = implicitly[Int] // search for an implicit value of type Int and assign it to c
<console>:6: error: could not find implicit value for parameter e: Int
val c = implicitly[Int]
^
Here are a few reasons to use the delightfully simple method implicitly.
To understand/troubleshoot Implicit Views
An Implicit View can be triggered when the prefix of a selection (consider for example, the.prefix.selection(args) does not contain a member selection that is applicable to args (even after trying to convert args with Implicit Views). In this case, the compiler looks for implicit members, locally defined in the current or enclosing scopes, inherited, or imported, that are either Functions from the type of that the.prefix to a type with selection defined, or equivalent implicit methods.
scala> 1.min(2) // Int doesn't have min defined, where did that come from?
res21: Int = 1
scala> implicitly[Int => { def min(i: Int): Any }]
res22: (Int) => AnyRef{def min(i: Int): Any} = <function1>
scala> res22(1) //
res23: AnyRef{def min(i: Int): Int} = 1
scala> .getClass
res24: java.lang.Class[_] = class scala.runtime.RichInt
Implicit Views can also be triggered when an expression does not conform to the Expected Type, as below:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Here the compiler looks for this function:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Accessing an Implicit Parameter Introduced by a Context Bound
Implicit parameters are arguably a more important feature of Scala than Implicit Views. They support the type class pattern. The standard library uses this in a few places -- see scala.Ordering and how it is used in SeqLike#sorted. Implicit Parameters are also used to pass Array manifests, and CanBuildFrom instances.
Scala 2.8 allows a shorthand syntax for implicit parameters, called Context Bounds. Briefly, a method with a type parameter A that requires an implicit parameter of type M[A]:
def foo[A](implicit ma: M[A])
can be rewritten as:
def foo[A: M]
But what's the point of passing the implicit parameter but not naming it? How can this be useful when implementing the method foo?
Often, the implicit parameter need not be referred to directly, it will be tunneled through as an implicit argument to another method that is called. If it is needed, you can still retain the terse method signature with the Context Bound, and call implicitly to materialize the value:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Passing a subset of implicit parameters explicitly
Suppose you are calling a method that pretty prints a person, using a type class based approach:
trait Show[T] { def show(t: T): String }
object Show {
implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString }
implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s }
def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase }
}
case class Person(name: String, age: Int)
object Person {
implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] {
def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")"
}
}
val p = Person("bob", 25)
implicitly[Show[Person]].show(p)
What if we want to change the way that the name is output? We can explicitly call PersonShow, explicitly pass an alternative Show[String], but we want the compiler to pass the Show[Int].
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)
Starting Scala 3 implicitly has been replaced with improved summon which has the advantage of being able to return a more precise type than asked for
The summon method corresponds to implicitly in Scala 2. It is
precisely the same as the the method in Shapeless. The difference
between summon (or the) and implicitly is that summon can return a
more precise type than the type that was asked for.
For example given the following type
trait F[In]:
type Out
def f(v: Int): Out
given F[Int] with
type Out = String
def f(v: Int): String = v.toString
implicitly method would summon a term with erased type member Out
scala> implicitly[F[Int]]
val res5: F[Int] = given_F_Int$#7d0e5fbb
scala> implicitly[res5.Out =:= String]
1 |implicitly[res5.Out =:= String]
| ^
| Cannot prove that res5.Out =:= String.
scala> val x: res5.Out = ""
1 |val x: res5.Out = ""
| ^^
| Found: ("" : String)
| Required: res5.Out
In order to recover the type member we would have to refer to it explicitly which defeats the purpose of having the type member instead of type parameter
scala> implicitly[F[Int] { type Out = String }]
val res6: F[Int]{Out = String} = given_F_Int$#7d0e5fbb
scala> implicitly[res6.Out =:= String]
val res7: res6.Out =:= String = generalized constraint
However summon defined as
def summon[T](using inline x: T): x.type = x
does not suffer from this problem
scala> summon[F[Int]]
val res8: given_F_Int.type = given_F_Int$#7d0e5fbb
scala> summon[res8.Out =:= String]
val res9: String =:= String = generalized constraint
scala> val x: res8.Out = ""
val x: res8.Out = ""
where we see type member type Out = String did not get erased even though we only asked for F[Int] and not F[Int] { type Out = String }. This can prove particularly relevant when chaining dependently typed functions:
The type summoned by implicitly has no Out type member. For this
reason, we should avoid implicitly when working with dependently typed
functions.
A "teach you to fish" answer is to use the alphabetic member index currently available in the Scaladoc nightlies. The letters (and the #, for non-alphabetic names) at the top of the package / class pane are links to the index for member names beginning with that letter (across all classes). If you choose I, e.g., you'll find the implicitly entry with one occurrence, in Predef, which you can visit from the link there.
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.