2

我正在使用generics-sop库。我想写一个具有以下类型的值:

values :: forall r. IsEnumType r => NP (K r) (Code r)

也就是说,对于构造函数没有任何参数 ( IsEnumType) 的 sum 类型,我想生成一个 n 元乘积 ( NP),它在每个点都包含相应的构造函数值。

例如,对于类型

{-# LANGUAGE DeriveGeneric #-}

import qualified GHC.Generics as GHC
import Generics.SOP

data Foo = Bar
         | Baz
         deriving (GHC.Generic)

instance Generic Foo

我想生产 n 元产品

K Bar :* K Baz :* Nil 

我相信解决方案将涉及转换带有每个构造函数的通用表示的 n 元乘积,所以我写了这个:

values :: forall r. IsEnumType r => NP (K r) (Code r)
values = liftA_NP (mapKK (to . SOP))  _

使用liftA_NPmapKK。但我不确定如何自己生成通用表示。

4

2 回答 2

1

也许有一种更简单的方法可以做到这一点,但我设法values通过使用一个辅助类型类来定义,该类型POSN基本上对空类型级列表的类型级列表执行归纳:

values :: forall r c. (Generic r, Code r ~ c, POSN c) => NP (K r) c
values = liftA_NP (mapKK (to . SOP)) posn

-- products of sums of nil
class POSN xss where
    posn :: NP (K (NS (NP I) xss)) xss   

instance POSN '[] where
    posn = Nil

instance (SListI2 xss, POSN xss) => POSN ('[] ': xss) where
    posn = let previous = posn @xss
            in K (Z Nil) :* liftA_NP (mapKK S) previous

内部NP的 s 总是Nil,因为它们对应于每个构造函数的参数,并且从来没有任何参数。

归纳步骤将列表其余部分的每个总和“加一”,在头部添加一个“零”。

使用示例:

ghci> :set -XTypeApplications
ghci> values @Foo
K Bar :* K Baz :* Nil
于 2018-12-09T18:22:36.970 回答
1

您可以使用现有的injectionsapInjs*功能。

apInjs'_NP :: SListI xs => NP f xs -> NP (K (NS f xs)) xs

您必须提供函数参数的乘积,在我们的通用情况下,每个组件都将应用于底层数据类型的构造函数之一。

但是因为我们假设一个枚举类型,所以这些构造函数都没有任何参数,我们可以在任何地方提供空的参数列表!

values :: forall r . IsEnumType r => NP (K r) (Code r)
values =
  map_NP
    (mapKK (to . SOP))
    (apInjs'_NP
      (cpure_NP (Proxy @((~) '[])) Nil)
    )
于 2018-12-09T18:54:39.767 回答