【问题标题】:How to extract type parameter from type如何从类型中提取类型参数
【发布时间】:2020-01-04 15:50:50
【问题描述】:

给定以下类:

abstract class Foo[B] 
abstract class Baz[B, F <: Foo[B]] {
    def get(foo: F): B
    // other methods
} 

我讨厌在Baz 中需要两个类型参数,而第一个是多余的。我想写这样的东西:

abstract class Baz[F <: Foo[B]] {
  def get(foo: F): B
}

我是否可以在 Baz 中引用 B 类型(F 的)而不使用多个类型参数?这感觉应该是可能的,但我似乎无法弄清楚语法。

【问题讨论】:

  • 不确定我是否理解这个问题。如果F 是上下文绑定到Foo,这意味着隐式Foo[F] 必须在范围内,可以通过implicitly [Foo[F]] 访问。
  • 我似乎无法暗中工作。我已经更新了我的例子,希望能更清楚。
  • 您确定要上下文边界吗?你能分享一个更真实的例子来解释你的最终目标吗?
  • 我正在制作一个游戏框架。我提供了一个抽象游戏和一个抽象玩家。游戏是绑定到特定类型玩家的上下文。我有对游戏及其播放器进行操作的函数,但我不想采用多个类型参数
  • @NathanMerrill 为什么需要传递两个类型参数?类型推断应该有帮助吗?您如何使用这些类,为什么它们是抽象的。你控制这两个班级吗?您是否考虑过使用 type members 而不是 type parameters?也许这些应该是typeclasses? - 没有更多上下文和真实示例很难提供解决方法。 - 但正如 jwvh 所说,在一般情况下,您必须传递两个类型参数。

标签: scala generics types


【解决方案1】:
  1. 你能让B 成为类型成员而不是类型参数吗?

    abstract class Foo { type B }
    abstract class Baz[F <: Foo] {
      def get(foo: F): F#B
      // other methods
    }
    

    如果你需要类型参数和类型成员,你可以使用辅助模式

    abstract class Foo { type B }
    // Foo.Aux[B] instead of Foo[B]
    object Foo {
      type Aux[B0] = Foo { type B = B0 }
    }
    abstract class Baz[F <: Foo] {
      def get(foo: F): F#B
      // other methods
    }
    
  2. 你能让F更高级和get多态吗? (看起来有点像“无标签最终”方法。)

    abstract class Foo[B]
    abstract class Baz[F[X] <: Foo[X]] {
      def get[B](foo: F[B]): B
      // other methods
    }
    
  3. 你能让Foo 成为一个类型类吗?

    abstract class Foo[F] {
      type B
    }
    object Foo {
      type Aux[F, B0] = Foo[F] { type B = B0 }
      def instance[F, B0]: Aux[F, B0] = new Foo[F] { type B = B0 }
    
      //instead of  class F1 extends Foo[B1]
      implicit val foo1: Aux[F1, B1] = instance
    }
    
    abstract class Baz[F](implicit val foo: Foo[F]) {
      def get: foo.B
      // other methods
    }
    

    abstract class Baz[F: Foo] {
      val foo: Foo[F] = implicitly
      def get: foo.B
      // other methods
    }
    
  4. 你能把这两个类型参数提取到一个新的类吗?

    abstract class Foo[B]
    
    abstract class Tuple {
      type B
      type F <: Foo[B]
    }
    
    abstract class Baz[T <: Tuple] {
      def get(foo: T#F): T#B
      // other methods
    }
    

    abstract class Baz[T <: Tuple](t: T) {
      def get(foo: t.F): t.B
      // other methods
    }
    

【讨论】:

  • 类型成员工作。我以前从未真正使用过它们,所以我有一个新的世界要探索。谢谢!
【解决方案2】:

我讨厌在 Baz 中需要两个类型参数,而第一个是多余的。

没有冗余。如果 Baz 代码引用了 2 个不同的类型,那么我们需要为它们设置 2 个不同的名称(FB,或 XY,或 THISTHAT,它没关系)。

如果 Baz 代码仅引用 1 种类型,但该类型需要限制为 Foo 已实现的内容,那么您可以添加该限制:

class Baz[X](implicit ev: Foo[X]) { ...

这可以简化为:

class Baz[X: Foo] { ...

如果无法推断类型,并且您希望简化实例创建,也许可以使用类型成员/别名。

abstract class Baz[B] {
  type F = Foo[B]
  def get(foo: F): B
}

但通常把事情写出来会更清楚。

abstract class Baz[B] {
  def get(foo: Foo[B]): B
}

但这当然排除了子类型的可能性。

【讨论】:

  • 不幸的是,我的代码确实引用了 F 和 B 类型。我会说得更清楚
  • 这完全是多余的:要创建一个 Baz,我必须声明 Baz[A, B[A]].A 在那里写了两次,并且没有提供额外的信息。
  • 是的,ABaz[A,B[A]] 中显得多余,但F &lt;: Foo[B] 意味着F 可以是Foo[B]any 子类型。所以如果B[Z]B[A] 的子类型,那么Baz[A,B[Z]] 是允许的。
猜你喜欢
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-21
  • 2013-06-14
  • 2022-07-27
  • 2021-05-01
相关资源
最近更新 更多