簡體   English   中英

在 Scala 中綁定通配符類型參數

[英]Bind wildcard type argument in Scala

在 Scala 2 中,您當然可以使用通配符或存在類型作為類型 arguments。但是,這意味着您並不總是有想要使用的類型的名稱。 這有時會導致奇怪的情況,您需要依靠類型推斷來規避顯式編寫類型。

這是我的意思的一個有點人為的例子:

case class Container[T](value: T) {
  def replace(value: T): Container[T] = Container(value)
}

def identity[T](container: Container[T]): Container[T] = {
  // A weird way of writing the identity function,
  // but notice that I have essentially given the name
  // `T` to the `_`
  container.replace(container.value)
}

var x: Container[_] = Container[Int](1)

// This works, but as far as I know, there's no way to explicitly
// pass the type for `T`. For example, something like identity[_](x) won't work.
identity(x)

// This also fails to work, but if we could assign a name to the `_`, like `T`,
// then it would become obvious that this should work.
// x.replace(x.value)

有沒有辦法更干凈地解決這個問題? 如果你能寫出類似這樣的東西,那就太好了:

let Container[T] = x.type in {
  // Now there is a type T in this scope,
  // and x has type `Container[T]`
}

據我所知,Scala 中不存在任何此類內容。是否有我缺少的功能。 另外,有沒有人知道其他語言的類似功能?

使用類型模式匹配(使用 2.13 測試):

case class Container[T](value: T) {
  def replace(value: T): Container[T] = Container(value)
}

val x: Container[_] = Container[Int](1)

val y: Container[_] = x match {
  case c => c.replace(c.value)
}

實際類型本身在代碼中沒有名稱,實際上是不可見的,但基本上是這樣的:

case class Container[T](value: T) {
  def replace(value: T): Container[T] = Container(value)
}

val x: Container[_] = Container[Int](1)

val y: Container[_] = x match {
  case c: Container[t] =>{
    val v: t = c.value
    c.replace(v)
  }
}

類型模式t綁定了存在量化類型,可以在后面的表達式中使用,這樣v: t就可以打出來了, c.replace(v)也可以打出來。


另請參閱以下相關問題:

另一種選擇是使T成為類型成員而不是類型參數。 在這種情況下,存在類型僅對應於Container而特定類型是Container { type T =... } (又名Container.Aux[...] )。 不幸的是,類型成員類型不能在 class 主構造函數中使用

case class Container(value: T) { // not found: type T
  type T
  //...
}

所以我用特征+工廠方法替換案例 class

trait Container {
  type T
  def value: T
  def replace(value: T): Container.Aux[T] = Container(value)
}
object Container {
  type Aux[_T] = Container { type T = _T }
  // factory method
  def apply[_T](_value: _T): Aux[_T] = new Container {
    override type T = _T
    override val value: T = _value
  }
}

val x: Container = Container[Int](1)

x.replace(x.value) // compiles

def identity[T](container: Container.Aux[T]): Container.Aux[T] =
  container.replace(container.value)

identity[x.T](x) // compiles

請注意,我將x val而不是var ,以便路徑相關類型xT有意義。

也許您更喜歡保留 case class,因為編譯器為 case 類生成的所有語法糖。 在這種情況下,我們可以引入一個額外的特征

trait IContainer {
  type T
  def value: T
  def replace(value: T): IContainer.Aux[T]
}
object IContainer {
  type Aux[_T] = IContainer { type T = _T }
}

case class Container[_T](value: _T) extends IContainer {
  override type T = _T
  override def replace(value: T): Container[T] = Container(value)
}

val x: IContainer = Container[Int](1)

x.replace(x.value) // compiles

def identity[T](container: IContainer.Aux[T]): IContainer.Aux[T] =
  container.replace(container.value)

identity[x.T](x) // compiles

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM