问题
How can I design a method which returns a path dependent type? In the following example, I deliberately want Vertex
to be path dependent on Tree
such that it is forbidden to mix vertices across trees (and this is just an example):
trait Tree {
trait Vertex
def root: Vertex
def addChild(parent: Vertex): Vertex
}
trait TreeFactory { def make: Tree }
Now the following cannot be constructed:
def test(f: TreeFactory): (Tree, Map[Tree#Vertex, Tree#Vertex]) = {
val t = f.make
var sq = IndexedSeq(t.root)
var m = Map.empty[t.Vertex, t.Vertex]
for( i <- 1 to 100) {
val p = sq(util.Random.nextInt(sq.size))
val c = t.addChild(p)
m += c -> p
sq :+= c
}
(t, m)
}
Because obviously the map I return should not have keys and values of type Tree#Vertex
but of the path dependent vertex...
error: type mismatch;
found : scala.collection.immutable.Map[t.Vertex,t.Vertex]
required: Map[Tree#Vertex,Tree#Vertex]
Note: t.Vertex <: Tree#Vertex, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Tree#Vertex`. (SLS 3.2.10)
(t, m)
^
If I try to decouple tree creation and parent-child map build-up:
def test(t: Tree): Map[t.Vertex, t.Vertex] = {
var sq = IndexedSeq(t.root)
var m = Map.empty[t.Vertex, t.Vertex]
for (i <- 1 to 100) {
val p = sq(util.Random.nextInt(sq.size))
val c = t.addChild(p)
m += c -> p
sq :+= c
}
m
}
This fails for another reason: "error: illegal dependent method type"
回答1:
My twisted mind came up with this one. I hope there is a more elegant solution:
trait Gagaism {
val tree: Tree
val map: Map[tree.Vertex, tree.Vertex]
}
def test(f: TreeFactory) = new Gagaism {
val tree = f.make
val map = {
var sq = IndexedSeq(tree.root)
var m = Map.empty[tree.Vertex, tree.Vertex]
for (i <- 1 to 100) {
val p = sq(util.Random.nextInt(sq.size))
val c = tree.addChild(p)
m += c -> p
sq :+= c
}
m
}
}
回答2:
You can enable experimental support for dependent method types with -Xexperimental -Ydependent-method-types
, I think.
回答3:
I don't understand the type system well enough to explain why your first attempt doesn't work, but this is the pattern I usually follow (with bounded abstract type members), which does compile. It'd be nice to see implementations of Tree
and TreeFactory
to be more confident though.
package trees
trait Tree {
trait Vertex
type V <: Vertex
def root: V
def addChild(parent: V): V
}
trait TreeFactory { def make : Tree }
object Test {
def test(f: TreeFactory): (Tree, Map[Tree#Vertex, Tree#Vertex]) = {
val t = f.make
var sq = IndexedSeq(t.root)
var m = Map.empty[t.Vertex, t.Vertex]
for (i <- 1 to 100) {
val p = sq(util.Random.nextInt(sq.size))
val c = t.addChild(p)
m += c -> p
sq :+= c
}
(t, m)
}
}
来源:https://stackoverflow.com/questions/6769503/returning-a-path-dependent-type