toList на бесформенном HList терпит неудачу, когда полученный экзистенциальный тип слишком сложен

Учитывая следующие определения:

class R[T]

class A
class B
class C

Это работает:

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

Пока это не так:

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]

Где я ожидаю:

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

Псевдорешение:

Поставьте неявно себя:

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

Это, конечно, не решение, так как оно устраняет все преимущества HLists (HList предоставляется вызывающим кодом вместе со всеми необходимыми имплицитами).

Пояснение

Я буду счастлив, если получу List[R[_]] в конце без границ типа.


person gzm0    schedule 23.05.2013    source источник
comment
Я считаю, что вы можете просто явно указать тип элемента результирующего списка: s2.toList[R[_ ›: A with B with C]]. Это уже значительное улучшение.   -  person Régis Jean-Gilles    schedule 23.05.2013
comment
Здесь есть проблема, которую я рассмотрю. Однако я вовсе не уверен, что более точный тип (R[_ >: A with B with C]) особенно желателен. Не лучше ли иметь номинальный супертип A, B, C и т. д. и сделать R ковариантным?   -  person Miles Sabin    schedule 23.05.2013
comment
@RégisJean-Gilles Действительно, и это решает проблему.   -  person gzm0    schedule 23.05.2013
comment
@MilesSabin Меня вообще не волнуют границы. Все, что я хочу, это R[_] в результате. Я не контролирую параметры типа, которые используются для R. Создание коварианта R действительно решает проблему, но кажется неправильным для того, что представляет R в моем случае. Кроме того, я не могу гарантировать, что в будущем T не окажется в контравариантной позиции в R.   -  person gzm0    schedule 23.05.2013


Ответы (2)


Код, который вы написали, должен просто работать как есть, создавая именно тот экзистенциальный тип, который вы ожидали. Было бы очень полезно, если бы вы открыли ошибку в shapeless, чтобы ее исправление не было забыто в следующем выпуске.

В то же время, если вам действительно нужен тип List[R[_]] (который кажется мне более полезным), вы можете запросить его явно гораздо более простым способом, чем ваш обходной путь,

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)

Учитывая, что этот менее точный тип, вероятно, будет более полезен в контексте, вы захотите продолжать использовать явный аргумент типа элемента для toList даже после исправления ошибки, поэтому я считаю, что это правильный ответ, а не обходной путь.

person Miles Sabin    schedule 24.05.2013
comment
Спасибо за ответ. Это работает, как указал @RégisJean-Gilles. Я зарегистрирую ошибку для этого. - person gzm0; 24.05.2013

Я только что отправил PR, в котором это исправлено.

Простой вызов s2.toList без явного указания типа должен возвращать тот же тип, как если бы вы создали (стандартный) список с теми же элементами и позволили scalac сделать вывод о типе списка для вас (это что-то вроде List[R[_ >: A with B with C]]).

person Alex Archambault    schedule 06.06.2014