Suppose there is a trait:
trait OuterTrait {
type InnerType
}
Now we can write non-generic function someAlgo
:
def pairToString[S, U](x: S, y: U): String =
"{" + y.toString + " in " + x.toString + "}"
def pairPrintln[S, U](x: S, y: U) {
println(pairToString(x, y))
}
def someAlgo(x: OuterTrait)(y: x.InnerType): x.InnerType = {
pairPrintln(x, y)
y
}
and series of generic functions:
def someAlgoObjObj[T <: OuterTrait](x: T)(y: x.InnerType): x.InnerType = {
pairPrintln(x, y)
y
}
def someAlgoObjType[T <: OuterTrait](x: T)(y: x.InnerType): T#InnerType = {
pairPrintln(x, y)
y
}
def someAlgoTypeType[T <: OuterTrait](x: T)(y: T#InnerType): T#InnerType = {
pairPrintln(x, y)
y
}
And one more generic function doesn't compile:
def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = {
pairPrintln(x, y)
y
}
It seems that: 1) someAlgo
and someAlgoObjObj
are the most correct functions; 2) and there is no sense to use generic function in this example at all.
And I would like to clarify some differences between generic functions above. Please, correct me, If I make errors.
So as I understand type T
corresponds to static type of x
(call it X
) or explicit type of generic call (I mean algo[Int]
for instance). That's why T#InnerType
corresponds to type in declaration of type X
. But x.InnerType
also corresponds to InnerType
of static type of x
. Where is the difference?
Further... someAlgoObjType
compiles, so it seems that x.InnerType
must be subtype of T#InnerType
. Then it is OK that someAlgoTypeObj
doesn't compile, since we can't make downcast implicitly. Though we can rewrite last one:
def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = {
pairPrintln(x, y)
y.asInstanceOf[x.InnerType]
}
UPD1: I found one difference between someAlgoObjObj
and someAlgoTypeType
if use them with explicit type parameter. If we write some class extending OuterTrait
:
class OuterIntClass extends OuterTrait{
type InnerType = Int
}
val x: OuterIntClass = new OuterIntClass()
val y: Int = 5
Then:
someAlgoObjObj[OuterTrait](x)(y) // OK
and next call doesn't work:
someAlgoTypeType[OuterTrait](x)(y)
T#InnerType
means "A InnerType belonging in some T" while x.InnerType
means "A InnerType belonging in a given x (of type OuterTrait)".
The key here in understanding these is in some T vs in a given x . You can interpret in some as some T but we don't which T instance , meaning that in a two Ts aren't necessarily the same, so, T#InnerType can't be proven to be equal to another T#InnerType.
Let's analyze the signatures:
/* 1 */ def someAlgoObjObj[T <: OuterTrait](x: T)(y: x.InnerType): x.InnerType = ???
/* 2 */ def someAlgoObjType[T <: OuterTrait](x: T)(y: x.InnerType): T#InnerType = ???
/* 3 */ def someAlgoTypeType[T <: OuterTrait](x: T)(y: T#InnerType): T#InnerType = ???
Now for the forth one:
def someAlgoTypeObj[T <: OuterTrait](x: T)(y: T#InnerType): x.InnerType = y
The signature reads: given x and InnerType belonging to some T , returns the InnerType belonging to this x . But in the implementation, we try to return y, which belongs to a T that's not necessarily the same as x, hence the compiler complains.
Little note about update .
someAlgoTypeType[OuterTrait](x)(y)
Failes because your method signature tells that it expects its y
parameter to conform to the type T#InnerType
and your y.type
is Int. To make it work you should change it's type to the following:
class OuterIntClass extends OuterTrait{
type InnerType = Int
}
val x: OuterIntClass = new OuterIntClass()
val y: x.InnerType = 5
Now y
's type satisfies type projection T#InnerType
and someAlgoTypeType[OuterTrait](x)(y)
compiles
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.