2013-07-08 27 views
1

我想这样做如下:如何给Scala编译器证明集合具有正确类型的元素?

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

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

然而,这并不编译。我不能满足于一个List[Any]因为我有利用其成员在那里可以转换成JValues值预计,在说:

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

有什么办法解决?

+1

HLists? (https://github.com/milessabin/shapeless) – adelbertc

+0

什么是错误信息?你试过吗?(42:JValue)::(“hello”:JValue):: Nil'? – huynhjl

回答

5

你可以写

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

如果从这些类型JValue,那么它不会键入检查的隐式转换。

不幸的是,你不能将它写为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的人可以修复它,如果它是poss IBLE。)

+0

是[这个答案](http://stackoverflow.com/a/17519454/406435)斯卡拉足够了吗? – senia

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来提出更优雅/复杂的解决方案。

2

|:的方法稍加改进Luigi Plingeanswer

你可以写

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)) 
相关问题