gzm0
gzm0

Reputation: 14842

toList on shapeless HList fails when resulting existential type too complex

Given the following definitions:

class R[T]

class A
class B
class C

This works:

val s1 = new R[A] :: new R[B] :: HNil
val r1 = s1.toList
// r1 of type: List[R[_ >: A with B]]

While this does not:

val s2 = new R[A] :: new R[B] :: new R[C] :: HNil
val r2 = s2.toList
// could not find implicit value for parameter toList:
// shapeless.ToList[shapeless.::[R[A],
// shapeless.::[R[B],shapeless.::[R[C],shapeless.HNil]]],Lub]

Where I expect:

// r2 of type: List[R[_ >: A with B with C]]

Pseudo solution:

Supply implicit yourself:

val r3 = s2.toList(ToList.hlistToList[R[A], R[B], ::[R[C], HNil],
                                      R[_ >: A with B with C]])

This is of course not a solution, as it eliminates the whole benefit of HLists (the HList is supplied by a caller together with all necessary implicits).

Clarification

I am happy if I get a List[R[_]] at the end without the type bounds.

Upvotes: 4

Views: 580

Answers (2)

Alex Archambault
Alex Archambault

Reputation: 985

I just submitted a PR that fixes this.

Just calling s2.toList, without explicitly specifying a type, should return the same type as if you created a (standard) list with the same items and let scalac infer the type of the list for you (that is something like List[R[_ >: A with B with C]]).

Upvotes: 0

Miles Sabin
Miles Sabin

Reputation: 23046

The code you've written ought to Just Work, as-is, producing the precise existential type that you were expecting. It'd be very helpful if you would open a bug against shapeless so that a fix for this doesn't get forgotten for the next release.

In the meantime, if the type you're really after is List[R[_]] (which seems more likely to be useful to me) then you can ask for it explicitly in a much simpler way than your workaround,

scala> import shapeless._
import shapeless._

scala> class R[T] ; class A ; class B ; class C
defined class R
defined class A
defined class B
defined class C

scala> val s2 = new R[A] :: new R[B] :: new R[C] :: HNil
s2: R[A] :: R[B] :: R[C] :: HNil = R@7a26bc5e :: R@518fdf9 :: R@2bc9e90c :: HNil

scala> s2.toList[R[_]]
res0: List[R[_]] = List(R@7a26bc5e, R@518fdf9, R@2bc9e90c)

Given that this less precise type is likely to be more useful in context, you'll want to continue using the explicit element type argument to toList even after the bug is fixed, so I consider this to be the right answer rather than a workaround.

Upvotes: 4

Related Questions