The repro in shapeless#902 above is very simplified from my real use-case; perhaps I should just do something different upstream to avoid this, but nevertheless that behavior seems unexpected.
More context on my real use-case, fwiw:
A
is actuallyOHList
("Options HList"), where each element in the underlyingHList
is wrapped inOption[_]
, and some relevant functionality is exposed.TC
is actually a comparator type-classCmp
, which defines logic for determining whether two instances of typeT
are equal:- If they are not equal, it returns a customizable output-type
Ξ
representing the computed diff.trait Cmp[T] { type Ξ; def apply(l: T, r: T): Option[Ξ] }
- In truth, it is an alias for a more general version,
CanEq[L, R]
, where the two types being compared can be different
- If they are not equal, it returns a customizable output-type
I am basically making an ADT of:
OptionsHList[L <: HList] := Empty[L] | NonEmptyOptionsHList[L]
- an
HList
where each element is wrapped inOption[_]
- split into cases where either:
- all elements are
None
, or - at least one element is
Some
- all elements are
- an
NonEmptyOptionsHList[H :: T] := (Some[H] :: OptionsHList[T]) | (None :: NonEmptyOptionsHList[T])
- an
OptionsHList
where at least one element isSome
:- can begin with a
Some
, and have any configuration ofNone
s andSome
s in the tail, or - can begin with
None
, in which case at least one element in the tail must beSome
- can begin with a
- an
Empty[H :: T] := None :: Empty[T]
Some rough "number of inhabitants" math, just based on which elements are Some
vs None
(ignoring how many possible values can inhabit Some
for each element-type):
|OptionsHList[L]| = 2^N
(where N is the length of HListL
), because:|Empty[L]| = 1
(one possible list ofN
None
s)|NonEmptyOptionsHList[L]| = 2^N - 1 = 2^(N-1) + (2^(N-1) - 1)
2^(N-1)
is the number of possibilities for the first case,Some[H] :: OptionsHList[T]
- each of the
N-1
elements in the tail can beNone
orSome
- each of the
2^(N-1) - 1
is the second case,None :: NonEmptyOptionsHList[T]
- the tail can be any of
2^(N-1)
possibleOptionsHLists
, except for the oneEmpty
instance
- the tail can be any of
The math also works if you take into account the number of inhabitants of each underlying element type, but I'll stop there π.