Я написал свою собственную реализацию гетерогенных списков (сначала я прочитал о гетерогенных списках здесь , и моя реализация очень похожа на их)
{-# LANGUAGE GADTs, DataKinds, TypeOperators #-}
data HList a where
(:>) :: a -> HList b -> HList (a ': b)
Nil :: HList '[]
infixr 6 :>
Это замечательно;однако, как только я обнаружил, что работаю с этими разнородными списками, я часто хотел выразить идею о том, где каждый тип принадлежит определенному классу типов.Мое наивное первое решение здесь - полностью переписать тип данных HList
для каждого нового класса типов.Вот для Eq
(только для примера; это не единственный класс типов, который меня интересует):
{-# LANGUAGE GADTs, DataKinds, TypeOperators #-}
data EqHList a where
(:>) :: (Eq a) => a -> EqHList b -> EqHList (a ': b)
Nil :: EqHList '[]
infixr 6 :>
У этого есть целый ряд проблем.Во-первых, мне нужно переписывать его каждый раз, когда я хочу новый класс типов.Кроме того, функции, которые работают с моими старыми гетерогенными списками, не работают с новыми.
Моим следующим решением было использование пустых классов типов.
{-# LANGUAGE GADTs, DataKinds, TypeOperators, FlexibleInstances, FlexibleContexts #-}
data HList a where
(:>) :: a -> HList b -> HList (a ': b)
Nil :: HList '[]
infixr 6 :>
class Eqed a
instance Eqed (HList '[])
instance (Eq a, Eqed (HList b)) => Eqed (HList (a ': b))
Здесь экземпляр Eqed
является HList
, все элементы которого являются экземплярами Eq
.Это, конечно, лучше, чем последнее решение, однако я все еще чувствую, что его не хватает.Мне все еще приходится копировать и вставлять кучу кода каждый раз, когда я хочу поговорить о классе другого типа.Я чувствую, что это тип проблемы, который можно решить с помощью программирования на уровне типов.
Есть ли лучший способ?