Reputation: 8412
I would like to do something like the following:
val foo = List[B <% JValue] = 42 :: "hello" : Nil
for the compiler to know that the members of my list can be converted to JValue
s.
However, this does not compile. I cannot settle for a List[Any]
because I have to use its members where values that can be converted into JValues are expected, say in:
def fun[A <% JValue](x: List[A]) = ...
Is there any way to solve this?
Upvotes: 1
Views: 152
Reputation: 38045
A little improvement of |:
method from Luigi Plinge's answer:
You could write
val foo: List[JValue] = 42 :: "hello" :: HNil
with an appropriate implicit conversion (using 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)
Test:
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))
Upvotes: 2
Reputation: 51109
You can write
val foo: List[JValue] = List(42, "hello")
If there is no implicit conversion from these types to JValue
then it won't type check.
Unfortunately you can't write it as 42 :: "hello" :: Nil
because the compiler isn't smart enough to know that you want to apply the conversions on each term (you could add a type annotation on each term, but that's messy). Each ::
method would have to somehow look ahead all the way to the end of the expression to check if some later method made it fit the type parameter.
However, if you want to add your own funky operator, you can constrain the types allowed by ::
:
implicit class pimp(xs: List[JValue]) {
def |: (x: JValue) = x :: xs
}
after which you can write stuff like this:
val foo = 42 |: "hello" |: Nil
// type is List[JValue]
(I tried parameterizing this so that it would infer the most specific common type, as ::
does, but with an upper bound the compiler didn't want to play ball - see here. Maybe someone with more Scala-fu can fix it, if it's possible.)
Upvotes: 5
Reputation: 1260
You can enable the conversions with a simple set of implicits.
class JValue
implicit intToJValue(x: Int) = new JValue
implicit stringToJValue(x: String) = new JValue
val xs: List[JValue] = List(1, "hello")
For your second question you can enable wholesale list conversion with:
implicit def listToJList[A <% JValue](xs: List[A]): List[JValue] = xs
def foo[A <% JValue](x: List[A]): List[JValue] = x
This above example only works if you have a uniform type, otherwise you will need to employ a more sophisticated means, a list of heterogeneous types will unify to List[Any] in most cases.
You could come up more elegant/complicated solutions using shapeless, most employing shapless.Poly and HList's.
Upvotes: 0