您定义
class TListGen ixs (g :: * -> *) where
genTList :: Proxy ixs -> g ix -> TList ixs g
但是这意味着该函数的调用者可以选择什么来实例化所有类型的变量,所以还特别如何实例ix
。
因此,例如,
genTList (Proxy :: Proxy '[Int, String]) (Just False)
将是这个函数的类型调用正确,选择g
是Maybe
和ix
是Bool
。但这不可能是正确的。我们需要传递一个足够多态的东西,它至少适用于类型级列表中出现的所有元素,或者更好,可用于ix
的任何可能选择。这是一个等级2多态类型所实现的:
class TListGen ixs (g :: * -> *) where
genTList :: Proxy ixs -> (forall ix . g ix) -> TList ixs g
这需要RankNTypes
语言扩展。
现在调用者只能传递参数类型为g
的多态的函数。所以通过Just False
将不再工作,但通过Nothing
会没事的。
这些案例的定义原则上可以,但实际上您可以删除OVERLAPS
附注,甚至是代理参数,因为没有任何重叠,并且可以从结果类型推断出ixs
,因为它作为参数出现到TList
数据类型:
class TListGen ixs (g :: * -> *) where
genTList :: (forall ix . g ix) -> TList ixs g
instance TListGen '[] g where
genTList _ = TNil
instance TListGen ixs g => TListGen (ix ': ixs) g where
genTList g = g :-: genTList g
现在我们可以尝试使用它:
GHCi> genTList Nothing :: TList '[ Int, String ] Maybe
不幸的是,这将导致一个错误,因为没有Show
instan CE:
• No instance for (Show (TList '[Int, String] Maybe))
arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
定义Show
实例TList
是可能的,但有点棘手。
我不知道这是否是主要的锻炼,但如果你确定只重用代码,那么这一切都是在generics-sop封装。
你TList
被称为NP
(与论点翻转顺序为),你的genTList
被称为pure_NP
,所以你可以写
GHCi> import Generics.SOP.NP
GHCi> pure_NP Nothing :: NP Maybe '[ Int, String ]
Nothing :* (Nothing :* Nil)
谢谢!这是半的锻炼确实,但主要是我想有更少的依赖关系,并保持它的简单。 –