惯用地在序列上使用 Scala 隐式

Using Scala implicits on a sequence, idiomatically

假设我正在编写应该易于扩展和使用而无需冗长语法的库代码。似乎可以使用隐式转换来避免冗长,就像在 Scala Collections 库中一样,但我正在努力将其应用于可遍历对象,如下所示。

我有一个特点:

trait Wrapped[T]
{
  def value : T
}

然后我有 class Foo,图书馆里的一把钥匙 class。 FooWrapped.

的任何列表构成
case class Foo[T <: Wrapped[_]](lst : Traversable[T]) {
  override def toString = lst mkString " " 
}

一个常见的用例是包装一个 Int 所以我提供一个 WrappedInt class:

case class WrappedInt(value : Int) extends Wrapped[Int]

使用这段代码,我可以像这样制作一个 Foo

val wrappedInts = Seq(1, 2, 3, 4) map { new WrappedInt(_) }
val someFoo = Foo(wrappedInts)

我不喜欢在此处包装额外的代码。我希望以下代码是等效的:

val foo = Foo(Seq(1, 2, 3, 4)) //should be a Foo[WrappedInt] - gives error

我定义了以下隐式:

object Conversions {
  implicit def intsToWrapped(lst : Traversable[Int]) = lst map { new WrappedInt(_) }
}

但是还是不行,我的val fooFoo构造函数参数改成implicit lst后编译报错。 Scala 书中说,隐式转换本质上允许将 x + y 更改为 convert(x) + y,其中 convert 是隐式转换。在我看来,我有完全相同的情况,这里一个参数的一次转换就足够了。我通过这样做验证:

val foo = Foo(Conversions.intsToWrapped(Seq(1, 2, 3, 4)))

为什么我的隐式没有被应用?在当前的 Scala 中是否有一种不同的、更惯用的方式让 Foo 用更少的代码构建?

编辑:添加 import Conversions._ 没有帮助,如果我理解正确的话,应该没有必要,因为这个例子在一个文件中。

我得到的具体编译器错误是:

val foo = Foo(Seq(1, 2, 3, 4))


inferred type arguments [Int] do not conform to method apply's type parameter bounds [T <: Wrapped[_]]
type mismatch; found : Seq[Int] required: Traversable[T]

指定类型以帮助进行类型推断,如下所示:

val foo = Foo[WrappedInt](Seq(1, 2, 3, 4))

为每个整数给出一条消息,如

type mismatch; found : Int(1) required: WrappedInt

您可以在 foo 的构造函数中指定隐式转换(曾经是视图绑定)。您必须指定集合元素 "viewable" 作为它们的包装版本,而不是集合本身。

trait Wrapped[T] {
  def value : T
}

// you can specify bounds on W if you want to be able to do something specific on the values, e.g. W <: MyClass
case class Foo[T, W](lst : Traversable[T])(implicit asWrapped: T => Wrapped[W]) {
  override def toString = lst.map(_.value) mkString " "
}

case class WrappedInt(value : Int) extends Wrapped[Int]

// This is your implicit conversion from Int to WrappedInt
implicit def intToWrapped(x : Int): WrappedInt = WrappedInt(x)

val wrappedInts = Seq(1, 2, 3, 4) map { new WrappedInt(_) }
val someFoo = Foo(wrappedInts)
val foo = Foo(Traversable(1, 2, 3, 4))