繁体   English   中英

Scala - 具有特征的泛型参数的类型推断

[英]Scala - type inference of generic parameter with traits

我正在使用Scala中的一个小型渲染引擎来学习该语言。 引擎的体系结构强烈基于特征,因此我可以根据需要添加和删除部分管道。 一些特征是:

更新 :更正了某些类型信息)

trait Composable { val parent:DisplayObject with Composite = null }
trait Composite extends Composable { val children:ArrayBuffer[DisplayObject with Composable] = ArrayBuffer() }
trait Position { var x = 0.0d; var y = 0.0d }
...

DisplayObject是一个用于组成这些特征的空类。

我的管道中的一个步骤是展平每个对象的层次结构。 我的第一枪是:( 更新 :我添加了身体)

def flatten(root:DisplayObject with Composite) : ArrayBuffer[DisplayObject] =
{
    def traverse(composite:Composite, acc:ArrayBuffer[DisplayObject])
    {
      acc += composite
      for(composable <- composite.children)
      {
        composable match {
          case com:Composite => traverse(com, acc)
          case _ => acc += composable
        }
      }
    }

    val flat = new ArrayBuffer[DisplayObject]
    traverse(root, flat)
    flat
  }
}

这很好,但是,当我调用这个函数时,我丢失了很多类型信息:

val root = new DisplayObject with Position with Composite
root.children += new DisplayObject with Composable with Position
val list = flatten(root)

list的类型现在是List[DisplayObject] 我失去了职位信息。 所以我考虑在混合中添加泛型:( 更新 :添加正文)

  def genericFlatten[T](root:T with Composite) : ArrayBuffer[T] =
  {
    def traverse(composite:T with Composite, acc:ArrayBuffer[T])
    {
      acc += composite
      for(composable <- composite.children)
      {
        composable match {
          case com:T with Composite => traverse(com, acc)
          case composable:T with Composable => acc += composable
        }
      }
    }

    val flat = new ArrayBuffer[T]
    traverse(root, flat)
    flat
  }

然而,调用this给了我这个奇怪的结果:返回列表的类型现在是List[DisplayObject with Position with Composite] ,这是错误的,因为树的一些子项(叶子)将不具有Composite特征。 我期待T型被引入DisplayObject with Position 没有?

我可能错了,但我对类型推断器的作用有一种误解:如果你没有指定类型,类型推理器会尝试解决它,但它不会取代你自己定义的类型。

我们来拍你的第一个镜头:

def flatten(root:DisplayObject with Composite) : List[DisplayObject] 

在这里设置返回类型。 此方法将始终返回List[DisplayObject]并且绝对无法推断。 签名完全完整。

我们来拍你的第二个镜头:

def flatten[T](root:T with Composite) : List[T] 

这里再次没有类型推断。 有泛型参数,编译器将检查通用值。 您可以在Java中编写此方法,它根本没有类型推断


如果我已正确解释您的答案,您希望在不丢失其类型的情况下展平children列表中的元素。 Hower,如果我们看一下复合特征:

trait Composite extends Composable { 
        val children:ArrayBuffer[Composable] = new ArrayBuffer[Composable] 
}

这里我们有一个val children ,其类型为ArrayBuffer[Composable] ,更具体地说,它具有类型ArrayBuffer[T] ,其泛型参数T = Composable 这是您在声明中强制执行的编译类型,以及静态类型编程语言(如Scala或Java),在程序执行期间不允许更改。

这是理解您的问题的关键点:尝试用Java来思考它。 如果你有一个List<Object>你可以放入一个Integer,但这不会把你的List<Object>变成List<Integer> 让我们打破您的代码,将子项分成两行。

val firstChildren:DisplayObject with Position = new DisplayObject with Position
root.children += firstChildren

在这里,只要您的firstChildren val超出范围,您就会丢失其类型信息。 如果您通过root.children访问它,您将不会知道它是DisplayObject with PositionDisplayObject with Position而只是一个Composable 此外,firstChildren是一个DisplayObject


免责声明:

* 您尝试的内容并非无足轻重,因为Composable和Composite类具有循环引用。 我一直打破它提供一些简单的工作代码,但我必须警告你,在你掌握类型系统之前,它会带你在Scala中获得一定的经验。 *

您需要以某种方式保留父项上子项类型的信息以及子项上父项的信息。 因此,您需要两个类型参数。

  trait Composable[K,T<:Composite[K,T]] {
    val parent:T
  }

  trait Composite[K,T<:Composite[K,T]] extends Composable[K,T] {
    val children:ArrayBuffer[K] = new ArrayBuffer[K]
  }

  trait Position { val x = 0.0d; val y = 0.0d }

  class DisplayObject

  def flatten[K,T<:Composite[K,T]](root:DisplayObject with Composite[K,T]) : List[K] =
  {
    root.children.toList
  }
  class ComposableDisplayObjectWithPosition extends DisplayObject with Position with        Composite[DisplayObject with Position,ComposableDisplayObjectWithPosition]{
      // dangerous
      val parent = this
    }

  def main(args:Array[String]){

    val root = new ComposableDisplayObjectWithPosition
    root.children += new DisplayObject with Position
    val list:List[DisplayObject with Position] = flatten(root)
    println(list)
  }

免责声明:我不是Scala专家,下面的论点是通用的(原谅你的双关语)。

签名func(param:T with X)表示如果func的实际参数是A类型,则需要A<:T A<:X 这些要求是完全独立的。

在类型系统中没有without 如果T是通用的,那么A without X就不推测是A without X ,没有这样的东西。 T被推断为A ,因为它是满足A<:T唯一最简单和最有用的方法。 分别检查A<:X

如果DisplayObject with Position with Composite传递给flatten ,则返回DisplayObject with Position with Composite 如果你通过Foo with Bar with Composite传递Foo with Bar with Composite ,你也会得到它。 你不能减Composite ,因为没有这样的东西without 我不知道为什么有人愿意。 我无法想象额外类型信息会受到伤害的情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM