【问题标题】:Why doesn't scala infer the type members of an inherited trait?为什么 scala 不推断继承特征的类型成员?
【发布时间】:2014-07-27 17:59:10
【问题描述】:

我有一组类型,每个类型都有自己的类型成员:

sealed trait FieldType {
    type Data
    def parse(in: String): Option[Data]
}
object Name extends FieldType { 
    type Data = String 
    def parse(in: String) = Some(in)
}
object Age extends FieldType { 
    type Data = Int 
    def parse(in: String) = try { Some(in.toInt) } catch { case _ => None }
}

而且我有一组在 FieldTypes 集合上运行的类型(使用样板文件而不是对 arity 进行抽象):

sealed trait Schema {
    type Schema <: Product
    type Data <: Product
    val schema: Schema
    def read(in: Seq[String]): Option[Data]
}
trait Schema1 extends Schema {
    type D1
    type FT1 <: FieldType { type Data = D1 }
    type Schema = Tuple1[FT1]
    type Data = Tuple1[D1]
    def read(in: Seq[String]) = schema._1.parse(in(0)).map(Tuple1.apply)
}
trait Schema2 extends Schema {
    type D1
    type D2
    type FT1 <: FieldType { type Data = D1 }
    type FT2 <: FieldType { type Data = D2 }
    type Schema = (FT1, FT2)
    type Data = (D1, D2)
    def read(in: Seq[String]) = {
        for {
            f <- schema._1.parse(in(0))
            s <- schema._2.parse(in(1))
        } yield (f, s)
    }
}

我想我可以使用这个系统优雅地定义有意义的字段集,因为 scala 将能够推断类型成员:

class Person extends Schema2 {
    val schema = (Name, Age)
}

但是,这不会编译!我必须包含所有类型成员的定义:

class Person extends Schema2 {
    type D1 = String; type D2 = Int
    type FT1 = Name.type; type FT2 = Age.type
    val schema = (Name, Age)
}

为什么 scala 不能推断出 D1,... 和 FT1,...?我怎样才能重构它,这样我就不必在Person 中指定类型变量?

注意:一旦我对宏有了更好的理解,我计划将它们用于Schema 类型。另外,我宁愿不使用无形。这是一个很棒的库,但我不想把它拉进来解决这个问题。

【问题讨论】:

  • 这可能是结构类型的类型推断的限制吗?您是否尝试过改用泛型进行重组?
  • 这确实适用于FieldType 上的泛型。但是,在字段类型上使用通用参数会使 FieldType 的工作变得非常混乱。我尝试将FieldType 分为两种类型:FieldType 和Parses[Data],但是编译器无法再次进行推理。

标签: scala scala-2.10


【解决方案1】:

通过声明:

val schema: Schema

您指定schema 必须是Schema 类型或其任何子类型。因此,知道schema 的类型,您无法推断Schema,因为它可能是schema.type 的任何超类型。

您可以通过完全颠倒事情来解决您的问题:根据schema.type定义类型别名:

trait Schema2 extends Schema {
    type Schema = (FieldType, FieldType)
    type FT1 = schema._1.type
    type FT2 = schema._2.type
    type D1 = FT1#Data
    type D2 = FT2#Data
    type Data = (D1, D2)
    def read(in: Seq[String]) = {
        for {
            f <- schema._1.parse(in(0))
            s <- schema._2.parse(in(1))
        } yield (f, s)
    }
}

(不确定它会实际上工作,但理论上这应该进行类型检查。)

【讨论】:

  • 使用这种方法,编译器似乎无法确定FT1#Data 与schema._1.parse() 的结果相同。不过,我正在尝试不同的变体,但看起来type FT1 = schema._1.type 应该真的有帮助。
  • @DanGallagher 我认为如果您声明type Schema = (FieldType, FieldType)(而不是&lt;:),这将有效。 IntelliJ 抱怨您在此处所说的内容,但编译工作正常。
  • 哦,哇,我自己都不太相信 ^^ 感谢您的修复,@Ben Reich。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
  • 2018-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
相关资源
最近更新 更多