How does a trait refer to the implementing class? - scala

For example, I want to declare a trait to say in effect, "If class C implements this trait, it has a method m that returns an instance of class C".
All the ways I can think of just say that m returns an instance having that trait, which is not what I want at all.

You constrain the "self type" like so:
scala> trait Foo[Self] { self:Self => def m:Self }
defined trait Foo
scala> class A extends Foo[A] { def m = new A }
defined class A
scala> class B extends Foo[A] { def m = new B }
<console>:27: error: illegal inheritance;
self-type B does not conform to Foo[A]'s selftype Foo[A] with A
class B extends Foo[A] { def m = new B }
^
<console>:27: error: type mismatch;
found : B
required: A
class B extends Foo[A] { def m = new B }
^
scala> class B extends Foo[B] { def m = new A }
<console>:27: error: type mismatch;
found : A
required: B
class B extends Foo[B] { def m = new A }

If I understood your question correctly, you must be looking for something like this:
trait Foo[T] {
def m: T
}
class A extends Foo[A] {
def m = {
getInstanceOfAFromSomewhere()
}
}
class B extends Foo[B] {
def m = {
getInstanceOfBFromSomewhere()
}
}
Testing:
object Test extends App {
val a: Foo[_] = new A
println(a.m)
val b: Foo[_] = new B
println(b.m)
}
Output:
A#3d246f44
B#14a1373f
Hope this is what you are looking for

Related

How can I assign a val with the implementation of a trait?

I have the following trait and classes:
trait A{
def func1: String
def func2: Int
}
class F extends A{
def func1: String = ???
def func2: Int = ???
}
class G extends A{
def func1: String = ???
def func2: Int = ???
}
And I want to create the following trait and class:
trait X{
val a: ***
}
class Z extends X{
override val a = new G
}
What do I need to write on '***' in trait X so that I can assign val a with a class F or G ?
Thank you.
EDIT
Sorry, I forgot to mention that trait A is A[Any] and traits F and G can be 'F extends A[String]' and 'G extends A[Int]'. In other words, the classes can extends the trait A with different types of parameters.
Re. your edit, your code fails because trait A only takes type parameter of a fixed type. If you declare base class having a A[Any], you cannot override it with a A[Int] or A[String]. There are two ways to make your code works.
One is to make trait A type argument covariant, so that you can override with subclass of the type argument you declared in base class (note the + in trait A definition):
trait A[+T]
class F extends A[String]
class G extends A[Int]
trait X {
val a: A[Any]
}
class Z extends X {
val a = new G
}
See here: Sample
Another way to work is to declare a as a A[_] (_ means it takes any type as a parameter, which is different from Any):
trait A[T]
class F extends A[String]
class G extends A[Int]
trait X {
val a: A[_]
}
class Z extends X {
val a = new G
}
See here: Sample

scala self generic illegal inheritance

Basicaly this is what I am trying to do.
A trait that has a member and another trait that inherits that member and initialize it.
Then a class that takes a generic type that must be inherited from the first trait and accesses that member
object main extends App{
val z = new c[b]()
z.p()
}
trait a{
val x : String
}
trait b extends a {
val x = 1
}
class c [A <: a] {
self: A =>
def p(): Unit ={
print(x)
}
}
but in the line
val z = new c[b]()
c[b] is underlined and the compiler complains that
"Illegal inheritance, self-type c[b] does not conform to A"
I want to define multiple traits that inherit from a father trait and then define a class that takes one of the fathers child traits and accesses its values
I found the answer, I simply had to extend the object while creating it, like this:
object main extends App{
val z = new c[b] with b
z.p()
}
trait a{
val x : String
}
trait b extends a {
val x = "1"
}
class c[A <: a]{
this: A =>
def p(): Unit ={
print(x)
}
}

Using and extended type in an overriden method

I'm trying to create an abstract class that has a method that I want to take a param that is the same as or extends some base class. I thought it would be:
trait InputParams
abstract class A() {
def input[T <: InputParams](datum: T) // abstract
...
}
case class ExtendedInputParams() extends InputParams
class B() extends A() {
override def input[T <: InputParams](datum: T)...
// the input(datum) method needs more to be able to treat datum as an
// ExtendedInputParams, how do I do this?
...
}
in class B (I suspect A needs changes too) how do I define the datum type as ExtendedInputParamsfor (new B).input(some-datum-of-type-ExtendedInputParams) so the overriding input can treat datum as an ExtendedInputParams and still enforce that it is extending InputParams? I also want to enforce that the method overrides the abstract method in class A.
updated for clarity
To narrow the constraint in the subclass:
scala> class P ; class Q extends P
defined class P
defined class Q
scala> class A { type T <: P ; def f[X <: T](a: X) = a.toString }
defined class A
scala> class B extends A { type T = Q ; override def f[X <: T](a: X) = a.toString * 2 }
defined class B
scala> (new B).f(new Q)
res0: String = Q#1ea930ebQ#1ea930eb
scala> (new B).f(new P)
<console>:14: error: inferred type arguments [P] do not conform to method f's type parameter bounds [A <: Q]
(new B).f(new P)
^
<console>:14: error: type mismatch;
found : P
required: A
(new B).f(new P)
^
scala> (new A { type T = P }).f(new P)
res3: String = P#4300e240
Reworded:
scala> class P ; class Q extends P { def q = 42 }
defined class P
defined class Q
scala> class X[T <: P] { def f[A <: T](a: A) = a.toString }
defined class X
scala> class Y[T <: Q] extends X[T] { override def f[A <: T](a: A) = a.q.toString }
defined class Y
scala> (new Y).f(new Q)
res0: String = 42

Scala type parameter bound error in subclass but not superclass

Why don't the following work?
scala> class Foo[B<:Foo[B]]
defined class Foo
scala> class Goo[B<:Foo[B]](x: B)
defined class Goo
scala> class Hoo[B<:Hoo[B]] extends Foo[Hoo[B]] { def f = new Goo(this) }
defined class Hoo
scala> class Ioo extends Hoo[Ioo] { def g = new Goo(this) }
<console>:11: error: inferred type arguments [Ioo] do not conform to class Goo's type parameter bounds [B <: Foo[B]]
class Ioo extends Hoo[Ioo] { def g = new Goo(this) }
^
scala> class Ioo extends Hoo[Ioo] { f } // yet this works!
defined class Ioo
this in new Goo(this) must be B <: Foo[B]. It is Ioo, so we need Ioo <: Foo[Ioo].
Ioo is Hoo[Ioo], hence Foo[Hoo[Ioo]] (inheritance of Hoo), which does not give a Foo[Ioo].

define method to return type of class extending it

I'd like to be able to do something like this:
trait A {
def f(): ???_THE_EXTENDING CLASS
}
class C extends A {
def f() = self
}
class D extends A {
def f() = new D
}
class Z extends D {
def f() = new Z
}
And the following would not compile, given the above code
class Bad1 extends A {
def f() = "unrelated string"
}
class Bad2 extends A {
def f() = new C // this means that you can't just define a type parameter on
// A like A[T <: A] with f() defined as f: T
}
class Bad3 extends D // f() now doesn't return the correct type
Is there a name for this kind of relationship? And how is it annotated/implemented in Scala?
Edit
The following sort of works, as you can see:
scala> trait A {
| def f: this.type
| }
defined trait A
scala> class C extends A {
| def f = this
| }
defined class C
scala> class D extends A {
| def f = new D
| }
<console>:7: error: type mismatch;
found : D
required: D.this.type
def f = new D
^
Is there a way to get around that?
Edit 2
Using the second system, I can do this, which is good up to the definition of class D:
scala> trait A[T <: A[T]] { def f(): T }
defined trait A
// OR
scala> trait A[T <: A[T]] { self: T =>
| def f(): T
| }
scala> class C extends A[C] { def f() = new C }
defined class C
scala> class D extends C
defined class D
scala> (new D).f
res0: C = C#465fadce
I'm afraid there is no possibility to know what is the extended class from the extending class.
The closest to what you'd like to have is something similar to Curiously Recurring Template Pattern (CRTP) well known from C++.
trait A[T <: A[T]] {
def f(): T;
}
class C extends A[C] {
def f() = new C
}
class D extends A[D] {
def f() = new D
}
One thing that you can do, is to return type this.type:
trait A {
def f(): this.type
}
class C extends A {
def f() = this
}
class D extends A {
def f() = this
}
class Z extends D {
override def f() = this
def x = "x"
}
println((new Z).f().x)
This can be useful for builders.
Here is another possible solution. It's combination of self type + type parameter:
trait A[T <: A[T]] { self: T =>
def f(): T
}
class Z extends A[Z] {
override def f() = new Z
def x = "x"
}
println((new Z).f().x)
Here you can find more info about this solution:
scala self-type: value is not a member error