如何给Scala编译器证明集合具有正确类型的元素?

时间:2013-07-08 00:49:41

标签: scala

我想做以下事情:

val foo = List[B <% JValue] = 42 :: "hello" : Nil

让编译器知道我的列表成员可以转换为JValue s。

但是,这不会编译。我无法满足于List[Any],因为我必须使用其成员,其中可以转换为JValues的值,例如:

def fun[A <% JValue](x: List[A]) = ...

有什么方法可以解决这个问题吗?

3 个答案:

答案 0 :(得分:5)

你可以写

val foo: List[JValue] = List(42, "hello")

如果没有从这些类型到JValue的隐式转换,那么它将不会键入check。

不幸的是你不能把它写成42 :: "hello" :: Nil,因为编译器不够聪明,不知道你想在每个术语上应用转换(你可以在每个术语上添加一个类型注释,但这很麻烦)。每个::方法都必须以某种方式一直向前看到表达式的结尾,以检查某个后来的方法是否使其适合类型参数。

但是,如果您想添加自己的时髦运算符,则可以约束::允许的类型:

implicit class pimp(xs: List[JValue]) {
  def |: (x: JValue) = x :: xs
}

之后你可以写这样的东西:

val foo = 42 |: "hello" |: Nil
  // type is List[JValue]

(我尝试对其进行参数化,以便它可以推断出最具体的常见类型,如::所做的那样,但是使用上限,编译器不想玩球 - see here。也许有人如果可能的话,更多Scala-fu可以修复它。)

答案 1 :(得分:2)

Luigi Plinge answer|:方法略有改进:

你可以写

val foo: List[JValue] = 42 :: "hello" :: HNil

使用适当的隐式转换(使用shapeless):

import shapeless._

trait HListTConv[H <: HList, T] {
  def apply(l: List[T], hl: H): List[T]
}

object HListTConv {
  implicit def apply0[T] = new HListTConv[HNil, T] {
    def apply(l: List[T], hl: HNil): List[T] = l
  }

  implicit def applyN[Head, Tail <: HList, T](implicit c: HListTConv[Tail, T], conv: Head => T) =
    new HListTConv[Head :: Tail, T] {
      def apply(l: List[T], hl: Head :: Tail): List[T] = (hl.head: T) :: c(l, hl.tail)
    }
}

implicit def hListToJValueList[H <: HList](hl: H)(implicit c: HListTConv[H, JValue]): List[JValue] = c(Nil, hl)

测试:

case class Test(s: String)
implicit def intToTest(i: Int): Test = Test(i.toString)
implicit def strToTest(s: String): Test = Test(s)

implicit def hListToListTest[H <: HList](hl: H)(implicit c: HListTConv[H, Test]): List[Test] = c(Nil, hl)

scala> val foo: List[Test] = 42 :: "hello" :: HNil
foo: List[Test] = List(Test(42), Test(hello))

答案 2 :(得分:0)

您可以使用一组简单的含义来启用转换。

class JValue
implicit intToJValue(x: Int) = new JValue
implicit stringToJValue(x: String) = new JValue

val xs: List[JValue] = List(1, "hello")

对于第二个问题,您可以通过以下方式启用批量清单转换:

implicit def listToJList[A <% JValue](xs: List[A]): List[JValue] = xs
def foo[A <% JValue](x: List[A]): List[JValue] = x

以上示例仅在您具有统一类型时才有效,否则您将需要采用更复杂的方法,在大多数情况下,异构类型列表将统一到List [Any]。

你可以使用无形的方式提出更优雅/更复杂的解决方案,大多数采用shapless.Poly和HList's。