Scala:使用表示类型

时间:2012-07-23 19:10:57

标签: scala

因为具有表示类型的特征是自引用的,所以声明变量包含该特征的实例有点困难。在这个例子中,我只是声明一个变量包含一个特征的实例,声明一个函数接受并返回该特征的实例,并用该变量调用该函数:

trait Foo[+A <: Foo[A]]
case class Bar() extends Foo[Bar]
case class Grill() extends Foo[Grill]

// Store a generic instance of Foo
val b: Foo[_] = if(true) {
  Bar()
} else {
  Grill()
}

// Declare a function that take any Foo and returns a Foo of the same type
// that "in" has in the calling context
def echoFoo[A <: Foo[A]](in: A): A = in

// Call said function
val echo = echoFoo(b)

失败并显示错误:

inferred type arguments [this.Foo[_$1]] do not conform to method 
echoFoo's type parameter bounds [A <: this.Foo[A]]
val echo = echoFoo(b)
           ^

现在,这是有道理的,因为[_]就像Any(我不完全理解的方式)。它可能需要的是Foo[Foo[_]],因此类型参数符合A <: Foo[A]的范围。但是现在有一个内部Foo具有不符​​合的类型参数,表明解决方案类似于Foo[Foo[Foo[Foo[...,这显然是不正确的。

所以我的问题可能可以归结为:“此变量是否包含任何合法Foo”的Scala语法是什么?

2 个答案:

答案 0 :(得分:2)

像这样的自引用类型参数有点问题,因为它们不是声音。例如,可以定义如下类型:

case class BeerGarden extends Foo[Grill]

如你所见,A&lt;:Foo [A]的界限不够紧。在这种情况下我更喜欢使用蛋糕模式和抽象类型成员:

trait FooModule {
  type Foo <: FooLike

  def apply(): Foo

  trait FooLike {
    def echo: Foo
  }
}

现在,您可以递归且安全地使用Foo类型:

object Foos {
  def echo(foo: FooModule#Foo) = foo.echo
}

显然,对于您可能希望用这些类型解决的所有问题,这不是一个理想的解决方案,但重要的观察是FooLike是一个可扩展的特性,因此您可以随时继续优化FooLike以添加成员您需要,而不违反类型成员要强制执行的约束。我发现,在我想要表示的类型集合未关闭的每个真实案例中,这都是人们可以做到的最好的情况。重要的是FooModule在实例构造函数的类型上进行抽象,同时强制执行“自我类型”。你不能抽象而不是抽象而不是抽象。

有关此类事情的一些其他信息(以及我自己早期与递归类型斗争的记录)可在此处获取:

https://issues.scala-lang.org/browse/SI-2385

答案 1 :(得分:0)

虽然我同意存在传播泛型的问题,但当你遇到这个问题时,你应该在屏幕上看到一个很大的警告,因为它通常表明设计不好。这些是关于该主题的一般性建议。

  • 如果您使用泛型,那么type参数就是有原因的。它允许您通过传入或接收类型A的参数以类型安全的方式与Foo [A]交互,并允许您对A进行约束。如果丢失了类型信息,则会丢失类型安全性,并且如果您不再需要通用类,则无需编写泛型类:您可以将所有签名更改为Any并进行模式匹配。

  • 在大多数情况下,通过使用“类型类”

  • 为集合实现CanBuildFrom方法,可以避免递归类型
  • 最后,类型投影(FooModule#Foo)几乎没有应用程序,您可能希望查看与路径相关的类型。然而,这些也几乎没有应用。