Если я правильно интерпретирую это ...
{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}
data Goat
data Monk
data Tiger
data T a where
TGoat :: T Goat
TMonk :: T Monk
TTiger :: T Tiger
data TSeq a where
TNil :: TSeq ((), (), ())
TG :: T Goat -> TSeq ((), m, t) -> TSeq (Goat, m, t)
TM :: T Monk -> TSeq (g, (), t) -> TSeq (g, Monk, t)
TT :: T Tiger -> TSeq (g, m, ()) -> TSeq (g, m, Tiger)
TCG :: T Goat -> TSeq (Goat, m, t) -> TSeq (Goat, m, t)
TCM :: T Monk -> TSeq (g, Monk, t) -> TSeq (g, Monk, t)
TCT :: T Tiger -> TSeq (g, m, Tiger) -> TSeq (g, m, Tiger)
Имейте в виду, что не предпринимались попытки сделать это годным к употреблению . Это невероятно неуклюже.
Теперь, учитывая эти значения:
x = TCG TGoat (TG TGoat (TT TTiger (TM TMonk TNil)))
y = TG TGoat TNil
И эта функция:
f :: TSeq (Goat, Monk, Tiger) -> a
f _ = undefined
... тогда только f x
будет печатать проверку, а не f y
.
N.B. Удаление элементов из последовательности оставлено читателю в качестве упражнения.
РЕДАКТИРОВАТЬ : После дальнейшего рассмотрения я решил, что вышеупомянутое не достаточно ужасно. Вот:
Какой-то таинственно знакомый шаблон:
data Yes = Yes
data No = No
class TypeEq' () x y b => TypeEq x y b | x y -> b
instance TypeEq' () x y b => TypeEq x y b
class TypeEq' q x y b | q x y -> b
class TypeEq'' q x y b | q x y -> b
instance TypeCast b Yes => TypeEq' () x x b
instance TypeEq'' q x y b => TypeEq' q x y b
instance TypeEq'' () x y No
class TypeCast a b | a -> b, b->a where typeCast :: a -> b
class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t->a->b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x = x
Немного общего взлома:
infixr 1 :*:
data NIL
data h :*: t = h :*: t
class TIns x ys r | x ys -> r
instance TIns x NIL (x :*: NIL)
instance ( TypeEq x h b
, TIns' b x h t r
) => TIns x (h :*: t) r
class TIns' b x h t r | b x h t -> r
instance TIns' Yes x h t (h :*: r)
instance (TIns x t r) => TIns' No x h t (h :*: r)
class TElem x ys r | x ys -> r
instance TElem x NIL No
instance (TypeEq x h b, TElem' b x h t r) => TElem x (h :*: t) r
class TElem' b x h t r | b x h t -> r
instance TElem' Yes x h t Yes
instance (TElem x t r) => TElem' No x h t r
Ааааааа и выплата:
data TSeq2 a b where
TNil2 :: TSeq2 NIL NIL
TCons2 :: (TIns x ys r) => T x -> TSeq2 xs ys -> TSeq2 (x :*: xs) r
class (TElem x ys Yes) => Has ys x
instance (TElem x ys Yes) => Has ys x
class HasAll ys xs
instance HasAll ys NIL
instance (ys `Has` h, ys `HasAll` t) => HasAll ys (h :*: t)
x2 = TCons2 TGoat (TCons2 TGoat (TCons2 TTiger (TCons2 TMonk TNil2)))
y2 = TCons2 TGoat TNil2
f2 :: (s `HasAll` (Goat :*: Tiger :*: Monk :*: NIL)) => TSeq2 q s -> a
f2 _ = undefined
Как и выше, f2 x2
проверки типов, в то время как f2 y2
не удается.
Это все еще грязно и довольно болезненно, но гораздо более универсально!
И просто чтобы доказать, что результат все еще может обрабатываться как обычный тип данных в других обстоятельствах, вот пример Show
:
instance Show (T a) where
show TGoat = "TGoat"
show TMonk = "TMonk"
show TTiger = "TTiger"
instance Show (TSeq2 a b) where
show TNil2 = "[]"
show (TCons2 x xs) = concat [show x, ":", show xs]
Так что теперь мы можем делать такие вещи, как show
только последовательности, которые содержат все три вида предметов:
showTotal :: (s `HasAll` (Goat :*: Tiger :*: Monk :*: NIL)) => TSeq2 q s -> String
showTotal = show