Scala:指定默认泛型类型而不是Nothing

dhg*_*dhg 13 generics scala

我有一对看起来像这样的课程.有一个Generator基于某些类级别值生成一个值,并GeneratorFactory构造一个Generator.

case class Generator[T, S](a: T, b: T, c: T) {
  def generate(implicit bf: CanBuildFrom[S, T, S]): S =
    bf() += (a, b, c) result
}

case class GeneratorFactory[T]() {
  def build[S <% Seq[T]](seq: S) = Generator[T, S](seq(0), seq(1), seq(2))
}
Run Code Online (Sandbox Code Playgroud)

你会注意到GeneratorFactory.build接受一个类型的参数S并Generator.generate产生一个类型的值S,但是没有类型S存储的类型Generator.

我们可以使用这样的类.工厂按顺序工作Char,并generate产生一个String因为build给出了一个String.

val gb = GeneratorFactory[Char]()
val g = gb.build("this string")
val o = g.generate
Run Code Online (Sandbox Code Playgroud)

这很好,并且String隐式处理类型,因为我们正在使用GeneratorFactory.


问题

现在问题出现了,当我想要建造一个Generator没有通过工厂.我希望能够做到这一点:

val g2 = Generator('a', 'b', 'c')
g2.generate // error
Run Code Online (Sandbox Code Playgroud)

但是我得到一个错误,因为g2有类型Generator[Char,Nothing]和Scala"无法使用类型为Nothing的集合构造类型为Char的元素类型的集合."

我想要的是一种告诉Scala"默认值"的S方法,Seq[T]而不是Nothing.借用默认参数的语法,我们可以将其视为:

case class Generator[T, S=Seq[T]]
Run Code Online (Sandbox Code Playgroud)

解决方案不足

当然,如果我们明确地告诉生成器它的生成类型应该是什么,它是有效的,但我认为默认选项会更好(我的实际场景更复杂):

val g3 = Generator[Char, String]('a', 'b', 'c')
val o3 = g3.generate  // works fine, o3 has type String
Run Code Online (Sandbox Code Playgroud)

我认为重载Generator.apply有一个通用类型的版本,但这会导致错误,因为显然Scala无法区分这两个apply定义:

object Generator {
  def apply[T](a: T, b: T, c: T) = new Generator[T, Seq[T]](a, b, c)
}

val g2 = Generator('a', 'b', 'c')  // error: ambiguous reference to overloaded definition
Run Code Online (Sandbox Code Playgroud)

期望的输出

我想要的是一种简单地构造一个Generator没有指定类型的方法,S并将其默认为Seq[T]以便我可以这样做:

val g2 = Generator('a', 'b', 'c')
val o2 = g2.generate
// o2 is of type Seq[Char]
Run Code Online (Sandbox Code Playgroud)

我认为这将是用户最干净的界面.

我有什么想法可以实现这一目标吗?

Tra*_*own 5

您是否有理由不想使用基本特征然后S根据需要缩小其子类?以下例如符合您的要求:

import scala.collection.generic.CanBuildFrom

trait Generator[T] {
  type S
  def a: T; def b: T; def c: T
  def generate(implicit bf: CanBuildFrom[S, T, S]): S = bf() += (a, b, c) result
}

object Generator {
  def apply[T](x: T, y: T, z: T) = new Generator[T] {
    type S = Seq[T]
    val (a, b, c) = (x, y, z)
  }
}

case class GeneratorFactory[T]() {
  def build[U <% Seq[T]](seq: U) = new Generator[T] {
    type S = U
    val Seq(a, b, c, _*) = seq: Seq[T]
  }
}
Run Code Online (Sandbox Code Playgroud)

我已经制作了S一个抽象类型,以使其更加偏离用户的方式,但您也可以将其作为类型参数.