This may seem like a silly question, so bear with me...
Consider this REPL session:
scala> trait T
defined trait T
scala> val t = new T
<console>:8: error: trait T is abstract; cannot be instantiated
val t = new T
^
scala> val t = new T {}
t: java.lang.Object with T = $anon$1#78db81f3
scala> class C
defined class C
scala> val c = new C
c: C = C#170a6001
We can use a trait just like a class, except that we have to add {} after the new T. In fact, we're essentially mixing T into java.lang.Object, which actually makes a lot of sense to me.
If we have members, again only the {} must be added:
scala> trait T2 { val s = "test" }
defined trait T2
scala> val t2 = new T2
<console>:8: error: trait T2 is abstract; cannot be instantiated
val t2 = new T2
^
scala> val t2 = new T2 {}
t2: java.lang.Object with T2 = $anon$1#6a688d6f
scala> t2.s
res0: java.lang.String = test
scala> class C2 { val s = "test" }
defined class C2
scala> val c2 = new C2
c2: C2 = C2#73ea7821
scala> c2.s
res1: java.lang.String = test
If we have abstract members then the trait declaration is actually shorter by a few characters and, more importantly, more consistent in my eyes (no need to remember to put abstract in front of your declarations):
scala> trait T3 { val s: String }
defined trait T3
scala> val t3 = new T3 { val s = "test" }
t3: java.lang.Object with T3 = $anon$1#1f2f0ce9
scala> abstract class C3 { val s: String }
defined class C3
scala> val c3 = new C3 { val s = "test" }
c3: C3 = $anon$1#207a8313
If you forget that you must define some of the members, both ways give you compile errors:
scala> val badt3 = new T3 {}
<console>:7: error: object creation impossible, since value s in trait T3 of type String is not defined
val badt3 = new T3 {}
scala> class BadC3 { val s: String }
<console>:8: error: class BadC3 needs to be abstract, since value s is not defined
class BadC3 { val s: String }
And if we try to do more complex things then the power of traits naturally becomes further apparent:
scala> val t4 = new T with T2
t4: java.lang.Object with T with T2 = $anon$1#479e0994
scala> val c4 = new C with C2
<console>:9: error: class C2 needs to be a trait to be mixed in
val c4 = new C with C2
So again I ask, why does Scala bother with classes at all when traits are apparently both simpler and more powerful?
I assume the reason is conceptual and actual compatibility with Java, but I wonder whether code compatability could have been maintained behind the scenes. As I understand it, Scala traits just become Java classes behind the scenes, so why couldn't the reverse happen and Scala consider Java classes to essentially be traits?
Related to all this, why not allow dropping the curly brackets when unnecessary? For example:
val t = new T
At that point, as a user, traits would be indistinguishable from current Scala classes, but of course better.
There are several differences between traits and classes:
a trait can not take constructor parameters. This limitation might be lifted at some point, but it's a hard problem. A trait may be inherited multiple times in a hierarchy, and each instantiation may give different values for the constructor parameters
a trait is compiled to a Java interface and an implementation class (carrying the concrete methods). This means it's a bit slower, because all calls go through interfaces, and if they're concrete, they are forwarded to their implementation
a trait with concrete members can't be nicely inherited in Java (it could, but it would look like an interface, therefore concrete members would still need to be implemented in Java).
I don't think the distinction between classes and traits will go away, mostly because of the last two items. But they may become easier to use if the first point is solved. Regarding instantiation without the {}, that's a convenience that could be added, but I personally wouldn't like it: each instantiation creates a new class (an anonymous one), and there should be an indication to the programmer that that's the case.
Related
I have following code:
class Outer {
class Inner
}
val outer1 = new Outer
val outer2 = new Outer
val a1 = new outer1.Inner
val a2 = new outer2.Inner
val a2: outer2.Inner = a1
println(a1.isInstanceOf[outer2.Inner])
I know that val a2: outer2.Inner = a1 has compiling error because of path dependent type, but I don't understand why println(a1.isInstanceOf[outer2.Inner]) will print true
Because outer1.Inner and outer2.Inner have the same erasure, which is written as Outer#Inner in Scala. isInstanceOf only works up to type erasure unless specified differently for a specific case (as it is for compound types).
This is similar to List("a", "b").isInstanceOf[List[Int]] being true: don't trust isInstanceOf unless you understand how type erasure works!
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.
Suppose I have a trait A and a class A1 that extends A:
trait A
class A1 extends A
and A1 has some unique property:
class A1 extends A { val hello = "hello" }
and I have a method that I want to handle all subclasses of trait A:
def handle:A = new A1
but, if I try to access unique properties defined in A1, understandably, it doesn't work:
scala> handle.hello
<console>:11: error: value hello is not a member of A
handle.hello
^
Once I'm done handling instances of subclasses of A as As, how do I once again access them with all their unique properties? How does this mechanism work?
There are various mechanisms of varying complexity available to deal with this, but possibly the easiest and most common would be pattern matching:
val a = get
...do stuff with a as an `A`...
a match {
case a1: A1 => a1.hello
...other case clauses for other subtypes of A, if required...
case _ => println("Not a known sub-type of A")
}
Another mechanism involves ClassTags and/or TypeTags (or Manifests, pre Scala 2.10 or so), with which I am less familiar.
One of the possible mechanisms is to define additional interfaces as traits. For example:
scala> class A
defined class A
scala> trait A1 { val hello = "hello" }
defined trait A1
scala> def handle:A with A1 = new A() with A1
handle: A with A1
scala> handle.hello
res0: String = hello
As far as I've learned, traits in Scala are similar to interfaces in Java except methods are allowed to have an implementation. Also, in contrast to Scala classes, you can't pass them arguments for construction.
So far, so good. But why am I allowed to instantiate them? Really, I don't see a good reason to allow this.
You don't really instantiate them. As you drew a parallel with Java, let's go further into it. You are able in Java to build a Anonymous class out of an abstract class or of an Interface. It is almost the same in Scala:
scala> trait A
defined trait A
scala> new A {}
res0: A = $anon$1#5736ab79
Note that the curly braces are mandatory when you create an object from a trait. For example, yon cannot do:
scala> new A
<console>:9: error: trait A is abstract; cannot be instantiated
new A
^
While it would works perfectly for a class:
scala> class B
defined class B
scala> new B
res2: B = B#213526b0
Of course if some elements in your trait are not implemented, you need to implement them when you create the object:
scala> trait C {def foo: Int}
defined trait C
scala> new C {}
<console>:9: error: object creation impossible, since method foo in trait C of type => Int is not defined
new C {}
^
scala> new C {def foo = 42}
res4: C = $anon$1#744957c7
I am a newcomer to Scala. In 2.7.7, the following code
abstract class C
case class CC() extends C
trait T
val c1 = CC()
val c2 = new CC() with T
println(c1.hashCode == c2.hashCode,c1 equals c2)
prints
(false,true)
whereas I would have expected
(false,false)
What am I missing? Thanks in advance.
Case class equality (particularly in Scala 2.8) equality and hash codes are based upon tuple and/or product equality, and do not currently take the class into account. There was a recent discussion on this matter on the scala-debate mailing list here: http://old.nabble.com/Possible-Collision-Issue-with-Product.hashCode-td27026790.html
For what it's worth, here's what it currently looks like in 2.8:
Welcome to Scala version 2.8.0.Beta1-RC6 (Java HotSpot(TM) Client VM, Java 1.6.0_16).
Type in expressions to have them evaluated.
Type :help for more information.
scala> abstract class C
defined class C
scala> case class CC() extends C
defined class CC
scala> trait T
defined trait T
scala> val c1 = CC()
c1: CC = CC()
scala> val c2 = new CC() with T
c2: CC with T = CC()
scala> println(c1.hashCode == c2.hashCode,c1 equals c2)
(true,true)
This behaviour is expected for equals since the Scala compiler overrides the equals method for case classes.
I am however unsure why the hashCode is different in Scala 2.7.7. Your example results in (true, true) using Scala 2.8.