2014-09-29 71 views
3

可以说我们有一个特征,它具有一些值和一些操作。从抽象特质方法中返回相同的类型

trait Foo { 
    type Self <: Foo 
    val x: Int 
    def withX(x: Int): Self 
} 

这是使用抽象类型实现的。我们有一个绑定在Self上的类型,可以像这样实现:

case class Foo1(x: Int) extends Foo { 
    type Self = Foo1 
    def withX(x: Int) = copy(x = x) 
} 

这很好。我们可以使用该方法,并且我们可以看到该类型是静态保存的。

scala> Foo1(10).withX(5) 
res0: Foo1 = Foo1(5) 

问题开始的时候,我们希望有特质的类型,而不是具体类型的操作:

object Foo { 
//Error:(13, 43) type mismatch; 
//found : f.Self 
//required: A 
// def setFive[A <: Foo](f: A): A = f.withX(5) 
} 

好了,我们不能完全做到这一点,因为编译器不知道是什么类型Foo#Self将被分配给。但我们知道它是同一类型的。

使用一个丑陋的做法当然正常工作:

object Foo { 
    // Ugly type signature 
    def setFiveValid[A <: Foo](f: A): A#Self = f.withX(5) 

    // Another ugly type signature 
    def setFiveValid2[A <: Foo](f: A): f.Self = f.withX(5) 
} 

两人都不表达的意图很清楚。

虽然我们可以使用类型类来解决它。

case class Foo2(x: Int) 

trait FooOps[A] extends Any { 
    def a: A 
    def withX(x: Int): A 
} 

object Foo2 { 
    implicit class Foo2Ops(val a: Foo2) extends AnyVal with FooOps[Foo2] { 
    def withX(x: Int) = a.copy(x = x) 
    } 
} 

object Foo { 
    // View bounds approach. 
    def setFiveValid3[A <% FooOps[A]](f: A): A = f.withX(5) 
} 

但是,这仍然是非常嘈杂。

有没有更好的方法来实现setFive

编辑1

自种的主要问题是这样的事情:

Error:(24, 11) type mismatch; 
found : app.models.world.WObject.WorldObjUpdate[self.Self] => app.models.world.WObject.WorldObjUpdate[self.Self] 
    (which expands to) app.models.game.events.Evented[(app.models.world.World, self.Self)] => app.models.game.events.Evented[(app.models.world.World, self.Self)] 
required: app.models.world.WObject.WorldObjUpdate[self.Self] => app.models.game.events.Evented[(app.models.world.World, Self)] 
    (which expands to) app.models.game.events.Evented[(app.models.world.World, self.Self)] => app.models.game.events.Evented[(app.models.world.World, Self)] 
      identity 
     ^

然后诉诸怪异的签名和样板再次:

def attackReachable(
    data: WObject.WorldObjUpdate[Self] 
): WObject.WorldObjUpdate[data.value._2.Self] 

回答

4

你可以沿着“F-界定量化”的道路走下去:

trait Foo[F <: Foo[F]] { 
    def withX(x: Int): F 
} 

object Foo { 
    def setFive[F <: Foo[F]](f: F): F = f.withX(5) 
} 

我用这很多成功,但它的代价是不得不在任何地方写F <: Foo[F]]

+0

是的,这不是更清洁,我忘了提及我试图避免这一点。基本上,我正在寻找一种最小化样板的解决方案。 – arturaz 2014-09-29 17:55:12

+0

祝你的搜索,我必须警告你;我已经尝试了所有其他场景,没有这种方法提供的“闭环”,你不会走得太远。没有循环的类型投影将会使返回类型无法做很多事情。 – 2014-09-29 18:12:33

1

最好的签名是你建议的路径依赖型:

// Another ugly type signature 
def setFiveValid2[A <: Foo](f: A): f.Self = f.withX(5) 

你甚至不需要类型参数。打字f作为Foo会做(除非你需要A为别人在你的真实语境的东西):

def setFiveValid3(f: Foo): f.Self = f.withX(5) 

这是不难看。相反,它是路径依赖类型的完美用途之一。当你说它没有明确表达意图时,我也不同意:你很清楚地表明结果将有你所提供论据的类型Self

相关问题