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
Related
can someone explain why the code below doesn't work?
scala> abstract class A[T] {}
defined class A
scala> class B {}
defined class B
scala> object C extends A[B] {}
defined object C
scala> def method(arg: A[Any]): Unit = {}
method: (arg: A[Any])Unit
scala> method(C)
<console>:14: error: type mismatch;
found : C.type
required: A[Any]
Note: B <: Any (and C.type <: A[B]), but class A is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
method(C)
I have an abstract class, here named A, and I'd like to be able to pass anything that extends A[AnythingHere] as an argument to the method method. In Java I would write public void method(A<?> arg) {}, but I don't know how to make it work with Scala, as there is no ?.
I also tried adding that + to the +T, but after testing and searching for what it does, I didn't find it helpful here. Just the warning disappeared
Thanks in advance
The code does not work because if you want to use generics in variant or contravariant in Scala, you have to declare it explicitly. Otherwise, the Java invariant behaviour is implemented.
Java is by default invariant. This means that if A < B, thenList[A] and List[B] do not have any hierachical relationship between them.
Scala lets you to use declare generic type as covariant or contravariant, which means
If you declare a generic as covariant, then if if A < B, thenList[A] < List[B]. To do this, you have to declare the generic using the syntax class A[+T]
If you declare a generic as contravariant, then if if A < B, thenList[A] > List[B]. To do this, you have to declare the generic using the syntax class A[-T]
You probably want to use: def method[T](arg: A[T])
This means that you don't need to change the variance of the class definition.
Using Shapeless, I tried to get a Generic[F] via:
import shapeless._
class F(x: Int)
but it failed:
scala> Generic[F]
<console>:20: error: could not find implicit value for parameter gen: shapeless.Generic[F]
Generic[F]
^
Can shapeless produce a Generic[F]? If so, how?
What representation would you want for F? You could say it should be HNil, but the x isn't be visible outside of the body of the class, so Shapeless makes the decision not to provide any instance at all. This is "just" a design decision—the right one in my view, but it's easy to imagine Shapeless providing the HNil instance for your class.
It would also be reasonably easy to define your own instance for F, which is possible because Generic is just another type class:
import shapeless._
class F(x: Int)
object F {
implicit val genericF: Generic.Aux[F, HNil] = new Generic[F] {
type Repr = HNil
def from(r: HNil): F = new F(0)
def to(t: F): HNil = HNil
}
}
As another answer notes, you can get Shapeless to provide an instance for you by changing your class to a case class. That's not the only way, though—you could also change the constructor parameter to a val or just remove it:
scala> class F(val x: Int)
defined class F
scala> shapeless.Generic[F]
res0: shapeless.Generic[F]{type Repr = shapeless.::[Int,shapeless.HNil]} = anon$macro$3$1#73e5dfa9
scala> class F()
defined class F
scala> shapeless.Generic[F]
res1: shapeless.Generic[F]{type Repr = shapeless.HNil} = anon$macro$5$1#4e0e355c
You couldn't make the parameter a var or make the class abstract, though (unless it was sealed and had case class or object implementations). Or rather you could, but then you'd have to define your own instances again, and you'd be breaking the contract in the Generic documentation, which says that the characterized type should be an "immutable data type that has a canonical way of constructing and deconstructing instances".
As far as I know the exact details of what kinds of arrangements of class definitions will get Generic instances isn't documented anywhere (and the source isn't easy reading), but it's pretty easy to test the limits you're interested in by trying out specific cases in the REPL.
Given the following:
scala> trait Bip[T]
defined trait Bip
scala> trait Bop[R <: Bip[R]]
defined trait Bop
My understanding is that R's upper limit is the type Bip[R]. If so, it's not clear to me what type can satisfy this constraint.
For example, if I want to create a Bop[Int], then doesn't the following least upper bound resolve to Any?
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> lub(List(typeOf[Int], typeOf[Bip[Int]]))
res22: reflect.runtime.universe.Type = Any
This looks like an example of F-bounded polymorphism (there's a brief explanation here). You cannot create things like Bop[Int] but it's easy to create a new recursively-defined type that satisfies the constraint:
scala> case class Foo(bar: String) extends Bip[Foo]
defined class Foo
scala> trait Baz extends Bop[Foo]
defined trait Baz
So the only types that can satisfy R are those that are defined to extend Bip parameterized to themselves.
Im trying to get the direct super classes / traits of a ClassSymbol. The method baseClasses() does not work for me as it also includes the super super.. types.
The java.lang.Class.getSuperclass() and java.lang.Class.getInterfaces() would actually be sufficient for my use case, but I can't find a way to go from ClassSymbol to java.lang.Class in macro context!
If you use macro - you can't obtain runtime-object Class for class which does not exist (loaded) in compile-time (so you can't have an access to compiling classess from there). But, you could write your own function:
def getDirectBase(a: ClassSymbol) = {
val base = a.baseClasses.toSet - a //`baseClasses` contains `a` itself
val basebase = base.flatMap {
case x: ClassSymbol => x.baseClasses.toSet - x
}
base -- basebase
}
Results (examples obtained using run-time reflection, but compile-time should be same here):
scala> class A
scala> class B extends A
scala> class C extends B
scala> typeOf[C].typeSymbol.asInstanceOf[ClassSymbol].baseClasses
res37: List[reflect.runtime.universe.Symbol] = List(class C, class B, class A, class Object, class Any)
scala> getDirectBase(typeOf[C].typeSymbol.asInstanceOf[ClassSymbol])
res38: scala.collection.immutable.Set[reflect.runtime.universe.Symbol] = Set(class B)
In Scala we use mix-in like this:
class C extends A with B
I understand this declaration as C is a subclass of A with B. Is this true? Or C is just subclass of both A and B(I don't think it's possible on JVM which doesn't support multi-inheritance)?
If A with B is a type, why doesn't this line work?
classOf[A with B]
Another reason why I consider A with B a type is the fact that it can be used in pattern match:
val c = new C
val u = c match { case a: A with B => 1 } // 1
Scala supports multiple inheritance via traits. Any class can extend 0 or 1 class, but can also "mix in" any number of traits. (There is a bit of compiler magic that rearranges things behind the scenes to conform to the JVM's limitations) The syntax is along the lines of
class MyClass extends [ClassOrTrait] with [Trait] with [AnotherTrait] with ...
So your class C definition is more like
class ((C extends A) with B) than like class (C extends (A with B))
A with B is a type, and can be used as a type alias, but the classOf method wants a class:
scala> type AB = A with B
defined type alias AB
scala> classOf[AB]
<console>:11: error: class type required but A with B found
classOf[AB]
^
vs
scala> class AB extends A with B
defined class AB
scala> classOf[AB]
res12: Class[AB] = class AB
I agree with #Dylan. A with B is only a type definition. However for it to work with classOf[T] , it needs to have a Java class or interface generated by Scala.
scala> trait A
defined trait A
scala> trait B
defined trait B
scala> trait AB extends A with B
defined trait AB
scala> class C extends A with B
defined class C
scala> type TypeAB = A with B
defined type alias TypeAB
scala> println(classOf[A])
interface $line3.$read$$iw$$iw$A
scala> println(classOf[B])
interface $line4.$read$$iw$$iw$B
scala> println(classOf[AB] )
interface $line5.$read$$iw$$iw$AB
scala> println(classOf[C])
class $line6.$read$$iw$$iw$C
scala> println(TypeAB)
<console>:8: error: not found: value TypeAB
println(TypeAB)
^
scala> classOf[TypeAB]
<console>:11: error: class type required but A with B found
classOf[TypeAB]
Also it is interesting that Scala does manage to match the with in case constructs.