该ixset
库似乎将“密钥生成函数”与“索引”混为一谈,这使得Indexable
该类比作者可能想要的更强大。(特别是,empty
允许其中已经有一些元素 - 这使得名称empty
有点奇怪!)您可以通过引入一个仅用于函数的新类型(因此不能包含任何元素):
data IxFun a = forall key. (Typeable key, Ord key) => IxFun (a -> [key])
ixFun' :: (Typeable key, Ord key) => (a -> [key]) -> IxFun a
ixFun' = IxFun
instance Contravariant IxFun where
contramap f (IxFun g) = IxFun (g . f)
ixFromIxFun :: IxFun a -> Ix a
ixFromIxFun (IxFun f) = ixFun f
然后你可以构建一些类型类支持,如下所示:
class IndexableFun a where funs :: [IxFun a]
-- turn any IndexableFun into an Indexable
defaultEmpty :: IndexableFun a => IxSet a
defaultEmpty = ixSet (map ixFromIxFun funs)
此类的实例看起来与 的实例非常相似Indexable
,但empty = ixSet [ixFun foo, ...]
您现在编写的是funs = [ixFun' foo, ...]
. 现在很容易编写您的实例:
instance (IndexableFun a, Typeable a) => IndexableFun (Keyed a) where
funs = ixFun' (\v -> [key v]) : map (contramap value) funs
instance (IndexableFun a, Typeable a) => Indexable (Keyed a) where
empty = defaultEmpty
您甚至可以轻松地将实现调整ixGen
为这种类型:
ixGen' :: forall proxy a b. (Data a, Ord b, Typeable b) => proxy b -> IxFun a
ixGen' _ = ixFun' (flatten :: a -> [b])
将这种方法集成到ixset
包本身中将是一种非常好的接触,并且不应该太难。虽然首先联系维护者,因为这可能是一个有点侵入性的变化:可能想要修改Indexable
类本身,而不是像上面描述的那样创建一个复杂的额外类加默认实例设置,这不会向后兼容。