I have the following code in scala:
trait A {
val foo: String => Int = { in =>
in.toInt
}
}
trait B extends A {
def bar(a: String) = {
foo(a)
}
}
class C(a: String) {
self: B =>
val b = bar(a)
}
val x = new C("34") with B
During instantiation of x I get NPE. Couldn't figure out why.
edit
NOTE: Can not figure out why foo of A trait doesn't get initialized
Please refer to the http://docs.scala-lang.org/tutorials/FAQ/initialization-order.html.
The only addition to this is that self-type makes class C abstract. So actually you do:
abstract class C(a: String) {
def bar(a: String): Int
val b = bar(a)
}
val x = new C("34") with B
You can try to replace it in your code and see same result. Some more info here.
In short: the linearization of new C with B will be (B <- A) <- C, so initalization is C -> (A -> B). Please refer to Class Initialization section section:
After the superclass constructor is executed, the constructors for each mixin trait are executed. Since they are executed in right-to-left order within the linearization, but the linearization is created by reversing the order of the traits, this means the constructors for the mixin traits are executed in the order that they appear in the declaration for the class. Remember, however, that when mixins share hierarchy, the order of execution may not be quite the same as how the mixins appear in the declaration.
In your case new C("34") with B equals to class K extends C("34") with B; new K. Note that self type of class C doesn't affect the initialization order.
Simplified example:
scala> trait C {def aa: String; println(s"C:$aa")}
defined trait C
scala> trait D {val aa = "aa"; println(s"D:$aa")}
defined trait D
scala> new C with D
C:null
D:aa
res19: C with D = $anon$1#2b740b6
Solution: If your foo is placed in third party library (so you can't make it lazy), you may just use mix-in instead of self-type or at least mix A into the C class:
trait A {
val foo: String => Int = { in =>
in.toInt
}
}
trait B extends A {
def bar(a: String) = {
foo(a)
}
}
class C(a: String) extends A {
self: B =>
val b = bar(a)
}
val x = new C("34") with B
The short answer on why you get a NullPointerException is, that initialization of C requires initializing b, which invokes the method stored in val foo, which is not initialized at this point.
Question is, why is foo not initialized at this point? Unfortunately, I cannot fully answer this question, but I'd like to show you some experiments:
If you change the signature of C to extends B, then B, as the superclass of C is instantiated before, leading to no exception being thrown.
In fact
trait A {
val initA = {
println("initializing A")
}
}
trait B extends A {
val initB = {
println("initializing B")
}
}
class C(a: String) {
self: B => // I imagine this as C has-a B
val initC = {
println("initializing C")
}
}
object Main {
def main(args: Array[String]): Unit ={
val x = new C("34") with B
}
}
prints
initializing C
initializing A
initializing B
while
trait A {
val initA = {
println("initializing A")
}
}
trait B extends A {
val initB = {
println("initializing B")
}
}
class C(a: String) extends B { // C is-a B: The constructor of B is invoked before
val initC = {
println("initializing C")
}
}
object Main {
def main(args: Array[String]): Unit ={
val x = new C("34") with B
}
}
prints
initializing A
initializing B
initializing C
As you can see, the initialization order is different. I imagine the dependency-injection self: B => to be something like a dynamic import (i.e., putting the fields of an instance of B into the scope of C) with a composition of B (i.e., C has-a B). I cannot prove that it is solved like this, but when stepping through with IntelliJ's debugger, the fields of B are not listed under this while still being in the scope.
This should answer the question on why you get a NPE, but leaves the question open on why the mixin is not instantiated first. I cannot think of problems that may occur otherwise (since extending the trait does this basically), so this may very well be either a design choice, or noone thought about this use case. Fortunately, this will only yield problems during instantiation, so the best "solution" is probably to not use mixed-in values during instantiation (i.e., constructors and val/var members).
Edit: Using lazy val is also fine, so you can also define lazy val initC = {initB}, because lazy val are not executed until they are needed. However, if you do not care about side effects or performance, I would prefer def to lazy val, because there is less "magic" behind it.
Declare A.foo to be a lazy val.
Related
package p {
trait A{ type B }
}
trait C {
val D: Seq[p.A]
def m(t: D. ...) = ???
}
I would like to refer to type B through D. How can I do this?
Note
The code that I inherited was:
package p {
trait A{ type B }
}
trait C {
val D: p.A
def m(t: D.B) = ???
}
This makes sense in that context and certainly compiles.
This code uses path-dependent types.
trait A { type B }
class A1 extends A { type B = String }
class A2 extends A { type B = Int }
In each of these types implementation uses different type B. So you cannot just refer to type B directly, only through a path from the value.
trait Foo {
type Bar
def operationA(bar: Bar) = X
def operationB(bar: Bar) = Y
}
val foo1: Foo = ...
val foo2: Foo = ...
Here I cannot know of foo1.Bar =:= foo2.Bar. I don't know anything about it. I only know that I could pass foo1.Bar into foo1.operationA or foo1.operationB and pass foo2.Bar into foo2.operationA or foo2.operationB.
This is used correctly in the original code because you are fixing D to be a specific immutable value (D). So the types working on this value are referring to it (D.B).
Seq of D cannot be used that way. You would have to do something like
package p {
trait A{ type B }
}
trait C {
val Ds: Seq[p.A]
def m(D: p.A)(t: D. ...) = ???
}
and then while working on Ds passing each value of if into m first, followed by a value derived from it (so that it would have this D. ... type) next.
Assume the following situation
I have a trait A and a trait B that both declare a value that has the same name and type.
A defines the value explicitly, while B defines it implicitly
A and B are from external libraries and can not be changed. I don't want to fork.
I want to use both of them in a class C that is in my own code
How can I get them to align inside class C?
I want B.foo to be the value from A.foo
// External code, cant touch
trait A{
val foo = "boom"
}
trait B{
implicit val foo: String
}
// My class
class C extends A with B {
//uh oh??
}
Update (with help from Jasper-M)
// External code, cant touch
trait A{
val foo = "boom"
}
trait B{
implicit val foo: String
def implBang()(implicit s: String) = s
def doTheBang() = implBang()
}
// My class
class C extends B with A {}
new C().doTheBang; // Prints "boom"
Now only question remaining, how would I get foo to be in the implicit scope for class C?
Ideally you can select the implementation you want with super[Name]. But for vals that doesn't work for some reason.
class C extends A with B {
override implicit val foo = super[A].foo
// error: super may not be used on value foo
}
So if you really need some implicit String in C I would suggest just letting linearization do its thing and define another implicit val.
class C extends A with B {
implicit val bar = foo
}
You can just override the implicit variable, like:
// My class
class C extends A with B {
override implicit val foo = "My Value"
}
I understand how scala addresses the diamond inheritance situation by considering the order of the traits mentioned. I am curious to understand how it solves the same issue for fields. Here is what I am trying to understand -
class A {print("A")}
trait B extends A {print("B") ; val x="b"}
trait C extends B {print("C")}
trait D extends A {print("D"); val x="d"}
object TraitsEx extends App {
var d = new A with B with D
println(d.x)
}
The above code does not compile.
Well not magically as you can see. If this was a property of A class, then you could override it - with class linearization, you already know, each with X, where X extends A would override values:
trait A {
val x = "a"
}
trait B extends A {
override val x = "b"
}
trait C extends A {
override val x = "c"
}
object Main {
def main(args: Array[String]): Unit = {
println((new A {}).x)
println((new A with B).x)
println((new A with B with C).x)
}
}
prints
a
b
c
However, when each class introduces its own x that compiler cannot prove to override other xs, then it will leave solving that problem to you. It also suggest one solution:
object TraitsEx extends App {
var d = new A with B with D { override val x = "d" }
println(d.x)
}
this way you would override all different xs and remove ambiguity.
Scala solves conflicting fields by making you solve it.
Error:(21, 16) <$anon: A$A123.this.A with A$A123.this.B with A$A123.this.D> inherits conflicting members:
value x in trait B of type String and
value x in trait D of type String
(Note: this can be resolved by declaring an override in <$anon: A$A123.this.A with A$A123.this.B with A$A123.this.D>.)
var d = new A with B with D
^
If one field is supposed to override the other than you are required to put that in the code. Without a specified override the compiler won't make any decisions for you.
I need overriding implicit in object res defing one from trait. The purpose is to define custom implicits in one place (trait B). Trait a is defined in external library. Is it possible?
trait t {
}
object m extends t
object q extends t
trait a {
implicit val o: t = m
}
trait b {
implicit val o: t = q
}
trait c {
def action(implicit v: t): Unit = {}
}
object res extends c with a with b {
//i need smth like override val o = super[b].o
val ololo= action
}
It is not possible to mix-in two unrelated traits that both contain a member with the same identifier. The Scala compiler has no way to resolve which one would take precedence in this scenario. Since types A and B are related, o does not even need to have the same type in both of them. Trait B needs to extend A and override o. There isn't any other way to override a member without using inheritance.
trait T
case object M extends T
case object Q extends T
trait A {
implicit val o: T = M
}
trait B extends A {
override implicit val o: T = Q
}
trait C {
def action(implicit v: T): Unit = println(v)
}
object Res extends C with B {
def call() = action
}
scala> Res.call()
Q
Since you are already mixing A into Res anyway, it is assumed that A is not difficult to extend, since Res must implement any other unmentioned abstract members.
Suppose I have next traits:
trait A {
val a: String = "a"
}
trait B {
def a: String = "b"
}
And I want to mix both of these traits into some class C
class C extends B with A
Compiler doesn't allow me to create such class because I have to override method a
I want to override it using for example only A's implementaion. How can I do this?
EDIT
scala> class C extends B with A {
| override val a = super.a
| }
<console>:10: error: super may be not be used on value a
override val a = super.a
^
The compiler can't possibly know which one you intend to use, therefore you must specify it like:
class C extends B with A {
override def a = super[A].a
}
This approach allows you to choose the parent directly, regardless the trait order.
However, the traits define a differently (val and def) thus you must choose only one. You should use either def or val in both traits (not mix them).
Provided you make a a def in the A trait, you can do
class C extends B with A {
override val a = super.a
}
val c = new C
c.a // "a"
This will work because A is extended after B, so super will be its implementation.