从类型级列表中创建值列表

时间:2017-05-29 14:17:09

标签: haskell type-level-computation

我有一个像这样的类型级别列表

data TList (ixs :: [*]) (f :: * -> *) where
  TNil  :: TList '[] f
  (:-:) :: f ix -> TList ixs f -> TList (ix ': ixs) f

我正在尝试使用现有的TList生成新的TList。 想法是有一个功能

genTList :: TList ixs f -> t -> TList ixs g

其中' t'是一些能够构造类型的值的函数' g x'在哪里' x'是列表中的一种类型' ixs'。

所以给定

data Foo x

和(某种)

generate :: forall x . Bar x

我可以得到类似的东西

genTList (Foo Int :-: Foo String) generate = Bar :-: Bar

所以基本上对于每个项目' x'在类型列表中我想要一个类型' Bar x' 并且还使用无参数构造函数构造其值,因为我知道 那个' Bar x'没有构造函数参数。

我试图实现一些东西(https://gist.github.com/lolepezy/30820595afd9217083c5ca629e350b55),但它不会(合理地)进行类型检查。

那我该怎么办呢?

1 个答案:

答案 0 :(得分:6)

您定义

class TListGen ixs (g :: * -> *) where
  genTList :: Proxy ixs -> g ix -> TList ixs g

但这意味着函数的调用者可以选择实例化所有类型变量的内容,特别是如何实例化ix

例如,

genTList (Proxy :: Proxy '[Int, String]) (Just False)

是此功能的类型正确调用,选择gMaybeixBool。但那不可能是正确的。我们需要传递一些足够多态的东西,它至少对于类型级列表中出现的所有元素起作用,或者甚至更好,可以选择ix。这就是rank-2多态类型实现的目的:

class TListGen ixs (g :: * -> *) where
  genTList :: Proxy ixs -> (forall ix . g ix) -> TList ixs g

这需要RankNTypes语言扩展名。

现在调用者只能传递参数类型g中的多态函数。因此传递Just False将不再有效,但传递Nothing就可以了。

案例的定义原则上是可以的,但你实际上可以删除OVERLAPS pragma甚至代理参数,因为没有任何重叠,并且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实例:

• 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)