In recent Haskell, I would include a little kitchen sink.
{-
Then I would define type membership
data (:>) :: [x] -> x -> * where Ze :: (x ': xs) :> x Su :: xs :> x -> (y ': xs) :> x
and now I have all the final sums without provoking a whole series of definitions like OneOfN:
data Sum :: [*] -> * where (:-) :: xs :> x -> x -> Sum xs
But to address Thomas' readability problem, I would use template synonyms. In fact, these kinds of things are the reason why I have pounded synonyms of patterns for many years.
You may have a funny version of Maybe :
type MAYBE x = Sum '[(), x] pattern NOTHING :: MAYBE x pattern NOTHING = Ze :- () pattern JUST :: x -> MAYBE x pattern JUST x = Su Ze :- x
and you can even use newtype to create recursive sums.
newtype Tm x = Tm (Sum '[x, (Tm x, Tm x), Tm (Maybe x)]) pattern VAR :: x -> Tm x pattern VAR x = Tm (Ze :- x) pattern APP :: Tm x -> Tm x -> Tm x pattern APP fs = Tm (Su Ze :- (f, s)) pattern LAM :: Tm (Maybe x) -> Tm x pattern LAM b = Tm (Su (Su Ze) :- b)
The newtype wrapper also allows you to make an instance declaration for types constructed in this way.
You can, of course, also use template synonyms to hide the Either iteration beautifully.
This method is not exclusive to sums: you can do this for products as well, and this pretty much happens in the de Vries and LΓΆh Generics-SOP libraries.
The big victory of this coding is that the data description is data of type (s), allowing you to compile a lot of deriving functionality without breaking the compiler.
In the future (if I have my own way), all data types will be defined, not announced, data type descriptions made up of data characterizing both the algebraic structure (allowing to calculate the general equipment) of the data and their appearance (as you can see what you do when working with a particular type).
But the future is already here.