Семейства типов не являются инъективными, что технически означает, что вы не можете переходить от результата к аргументам справа налево. Кроме нет. В GHC 8.0 введена TypeFamilyDependencies
, которая позволяет вам указывать инъективность для семейств типов, например:
type family Add (el :: k) (set :: Set k) = (set' :: Set k) | set' -> el set where
Add el ('S xs) = 'S (el ': xs)
Однако, по некоторым причинам, которые я до сих пор не до конца понимаю, это все еще не работает в вашемслучай, вызывающий ту же проблему. Я подозреваю, что это может иметь какое-то отношение к тому факту, что рассматриваемый список имеет двойную оболочку, не уверен.
Но у меня действительно есть другой обходной путь: вы можете отказаться от всей проблемы с инъекцией и указать свой тип семейства другойнаоборот - из списка в кортеж. За исключением того, что вам понадобятся два семейства типов - одно для головы и одно для хвоста:
type family Head set where Head ('S (el ': xs)) = el
type family Tail set where Tail ('S (el ': xs)) = 'S xs
uncons :: SSet set -> (Proxy (Head set), SSet (Tail set))
uncons (SS (x `SCons` xs)) = (x, SS xs)
Но мне это кажется слишком сложным. Если вам просто нужно отключить эти наборы типов, я бы выбрал хороший класс типов ol, который обладает непревзойденным преимуществом объединения типов и значений вместе, так что вам не нужно перепрыгивать через обручи, чтобы сопоставлять их вручную:
class Uncons set res | set -> res where
uncons :: SSet set -> res
instance Uncons ('S (el ': xs)) (Proxy el, SSet ('S xs)) where
uncons (SS (x `SCons` xs)) = (x, SS xs)