Can a Scala type parameter reference itself be used when using a base class as a parameter?

If I have the following hierarchy of Scala types:

// Base traits trait TA[X <: TA[X,Y], Y <: TB[X,Y]] trait TB[X <: TA[X,Y], Y <: TB[X,Y]] trait TC[X <: TA[X,_]] // More specific traits trait TI[X <: TI[X,Y], Y <: TJ[X,Y]] extends TA[X,Y] trait TJ[X <: TI[X,Y], Y <: TJ[X,Y]] extends TB[X,Y] trait TK[X <: TI[X,_]] extends TC[X] // Concrete class that should implement TK, but does not compile class Z extends TK[TI[_,_]] // What is needed to be able to get class Z to compile // The reference of X to itself has been removed. trait TC2[X <: TA[_,_]] trait TK2[X <: TI[_,_]] extends TC2[X] class Z2 extends TK2[TI[_,_]] 

TC will be the general manager of some TA.

TK will be the more specific manager of the more specific TA (TI).

Z will be a concrete implementation that governs any object that implements TI.

Z is not legal, but Z2 is. Unfortunately, TC and TK are more specific than TC2 and TK2. So, is there a way to declare Z using TC and TK instead of TC2 and TK2?

[EDIT] In my initial question, I could not say that I understand why Z is unfounded. I really want to know if there is a way to say something like:

 class Z3 extends TK[TI[TI,_]] 
+6
source share
2 answers

If you have complex, mutually recursive type restrictions, it is often useful to see if you can translate your problem into an equivalent one using elements of an abstract type. If we do it mechanically, your base and more specific features will end up looking like

 // Base traits trait TA { type X <: TA type Y <: TB } trait TB { type X <: TA type Y <: TB } trait TC { self => type X <: TA { type X <: self.X } } // More specific traits trait TI extends TA { type X <: TI type Y <: TJ } trait TJ extends TB { type X <: TI type Y <: TJ } trait TK { self => type X <: TI { type X <: self.X } } 

and now we have a direct definition of Z as,

 class Z extends TK { type X = TI } 

Note that the definitions of the terms TA, TB, and TI, TJ are essentially the same. Since these types are now members of the type, we can decompose them into common base types, for example,

 // Base traits trait T0 { type X <: TA type Y <: TB } trait TA extends T0 trait TB extends T0 trait TC { self => type X <: TA { type X <: self.X } } // More specific traits trait T1 extends T0 { type X <: TI type Y <: TJ } trait TI extends TA with T1 trait TJ extends TB with T1 trait TK extends TC { self => type X <: TI { type X <: self.X } } class Z extends TK { type X = TI } 
+7
source

That would be unreasonable. That is why, with a simplified example. We don’t need two common parameters, we don’t need the subtypes TI, TJ and TK either

 trait TA[X <: TA[X]] trait TC[X <: TA[X]] class Z extends TC[TA[_]] 

arguments of type [TA [_]] do not match the parameter of type TC of the border [X <: TA [X]]

Let us see why this expression is unfounded, and it is correct that it fails. Add code. I change TC to class so that the code can be translated into java. A trait will also work in scala.

 trait TA[X <: TA[X]] {def f(x: X) } class TC[X <: TA[X]] {def g(x: X) = xf(x)} 

It works fine, x: X also TA[X] , so there is a subroutine f that takes X

If we try instead

 class TC2[X <: TA[_]] {def g(x: X) = xf(x)} 

then he fails. We know that there is an f method X , but we don’t know what type is needed as an argument, we cannot know that X will be fine. Indeed, suppose that

 class T1 extends TA[T1]] {def f(t1: T1) = {}; def t1Only = println("only in T1")} class T2 extends TA[T1]] {def f(t1: T1) = t1.t1Only } 

Now, if TC2 was allowed, I could create TC2[T2] , call g with T2 , which will call f in T2 with T2 . This is unacceptable, and correct, since T2 does not have the t1Only method.

This shows why TC cannot accept TA[_]] as its parameter, since it would allow T2 , which is incompatible with method g . So why can't we define Z with the parameter TA[_] . That would be exactly the same in java and with your source code.


Change I'm a little to blame for my answer. For the reason I gave why this should not be resolved, I thought there would be an easy way around the problem. This failed, I did not have time for further investigation and posted it without mention. The workaround was on its own. If we do

 trait TA[X <: TA[X]] {self: X => } 

then we cannot determine T2 extends TA[T1] . So there is more limited source code. But we should still accept restrictions on the source code, because it was unreasonable. Thus, this cannot be just a syntactic trick; it must make things impossible, which are not. I thought that the T2 extends TA[T1] was probably not what was intended, and that this was the only thing to prevent.

Apparently, this was not the same mistake. Now I have no example why this should not work. Which, of course, does not mean that they are not.

Then I looked at Miles' solution, wondering why the ability of T2 extends TA[T1] would not hurt him. So, dropping TB , Y , TI , TJ and TK :

 trait TA{type X} trait TC{self => type X <: TA{type X <: self.X} class Z extends TC{type X = TA} 

It compiles. And we can do

 trait T1 extends TA{type X = T1} trait T2 extends TA{type X = T1} 

But we can do nothing:

 trait TA {type X <: TA; def f(x: X)} trait TC {self => type X <: TA{type X <: self.X} def g(x: X) = xf(x) } 

We get the following error on g , for argument X f

type mismatch; found: x.type (with base type TC.this.X) required: xX

TC not original (as originally, g allowed). This is because TA has type X <: self.X , and TC[X <: TA[X]] is stronger than type X = self.X If we write that instead we return to the original error, Z does not compile. So, this TC somewhat different from the original TC ( type X = self.X ) and TC2 (without knowledge of X of TA). Again, a restriction on the source code, we cannot define g .

If the limit is acceptable, you're fine. I do not know how to write it as a generic one (and how to write a type self {self : X => with an abstract type member). Miles is definitely an expert, I’m sure that he will be able to tell how this is done or that this is not possible.

+3
source

Source: https://habr.com/ru/post/896027/


All Articles