2013-07-08 31 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)) 
相關問題