【问题标题】:Scala: how to work with long type parameter listsScala:如何使用长类型参数列表
【发布时间】:2012-02-12 07:01:01
【问题描述】:

我可以在 Scala 中使用哪些技术来处理长类型参数列表?

我正在开发一个小框架,用于在不同的模拟环境中运行各种类型的游戏。我试图保持框架的某些部分相对通用,所以我引入了各种类型作为类型参数,例如环境状态、游戏结果等。

这一切在功能上都运行良好,并且我确实获得了类型安全但通用框架的预期好处。但是类型签名已经发展到使代码难以阅读并且重构它变得非常麻烦的地步。顶级 Simulator 的签名有 8 个类型参数,许多主要类型有 3 到 5 个。个别编译器类型错误,因为它们列出了类或函数参数的类型(当然也是类型参数化的)似乎经常运行到一百行。

偶尔,但很少,我可以省略类型参数,例如关于构造函数。但在大多数情况下,不会推断出至少一种类型,因此我最终不得不插入整个类型签名。

显然这并不理想,我正在寻找解决此问题的方法。任何建议将不胜感激!

【问题讨论】:

  • 几行示例代码总是值得赞赏的 ;)
  • 这是一个非常普遍的问题。我不确定示例代码会增加很多价值。这只是通常的东西:特征、类、函数,每个都有类型参数,字段/成员也有类型参数。

标签: generics scala polymorphism


【解决方案1】:

想到了两个解决方案。

  1. 使用类型别名。

    scala> class Foo[A, B, C, D, E]
    defined class Foo
    
    scala> type Bar[A] = Foo[A, Int, Int, Int, Float]
    defined type alias Bar
    
    scala> new Bar[String]
    res23: Foo[String,Int,Int,Int,Float] = Foo@f590c6
    
  2. 使用abstract type members 代替类型参数。

    scala> class Bar {
         |   type A
         |   type B <: AnyVal
         |   type C
         | }
    defined class Bar
    
    scala> new Bar {
         |   type A = String
         |   type B = Int
         |   type C = Int
         | }
    res24: Bar{type A = String; type B = Int; type C = Int} = $anon$1@ee1793
    
    scala> trait Baz {
         |   type A = String
         | }
    defined trait Baz
    
    scala> new Bar with Baz {
         |   type B = Int
         |   type C = String
         | }
    res25: Bar with Baz{type B = Int; type C = String} = $anon$1@177c306
    
    scala> null.asInstanceOf[res25.A]
    res26: res25.A = null
    
    scala> implicitly[res25.A =:= String]
    res27: =:=[res25.A,String] = <function1>
    

您可能想与我们分享一些代码,以便我们提供更具体的建议。

【讨论】:

  • 我已经使用的第一个答案;如果你能以某种方式使用定义的类型别名来访问底层类型的构造函数,那就更好了。第二个很有趣;我将对此进行探索。
【解决方案2】:

考虑当你有一组密切相关的类型参数的情况;您将它们作为类型参数一起传递。在本例中,该组为AB

trait X[A, B, C] {
  new Y[A, B, Int] {}
}

trait Y[A, B, D] {
  def a: A = sys.error("")
}

您可以将这两种类型打包成一个类型参数,其中类型别名包含类型成员:

type AB = { type A; type B }
trait Y[ab <: AB, D] {
  def a: ab#A = sys.error("")
}
trait X[ab <: AB, C] {
  new Y[ab, C] {}
}
new X[{ type A=Int; type B=Int}, String] {}

【讨论】:

  • 哦,这很整洁!我会尽快尝试的!
  • 您还保留了通过这种方法使用方差的能力。
  • 如何指定方差?在您的示例中,例如如果 Y 最初是特征 Y[+A,-B]?
  • 谢谢。这真的非常宝贵。使用这种方法,我能够减少大约 15-20% 的整体代码大小,并使代码更容易重构。编译器现在崩溃了很多,但至少这是可以修复的。 ;-) 我在哪里可以阅读更多关于这种类型级编码的信息?我在 Odersky/Spoon/Venners 的书中找不到任何内容。
  • 嗯,不确定是否可以使A 协变和B 逆变;我只考虑所有相关类型参数具有相同方差的情况。然后,trait Y[+ab &lt;: AB, D].
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
  • 1970-01-01
  • 1970-01-01
  • 2017-06-29
  • 1970-01-01
  • 2020-05-05
  • 1970-01-01
相关资源
最近更新 更多