使用简单总和类型的所有值构建 n 元积
Constructing a n-ary product with all the values of a simple sum type
我正在使用 generics-sop 库。我想写一个具有以下类型的值:
values :: forall r. IsEnumType r => NP (K r) (Code r)
也就是说,对于其构造函数没有任何参数的求和类型(IsEnumType
) I want to produce an n-ary product (NP
) which contains每个点对应的构造函数值。
例如,对于类型
{-# 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)) _
也许有更简单的方法,但我设法通过使用辅助类型类 POSN
来定义 values
,它基本上对空类型级列表的类型级列表执行归纳:
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
总是Nil
,因为它们对应于每个构造函数的参数,而且从来没有任何参数。
归纳步骤 "adds one" 到列表其余部分的每个总和,在头部添加一个 "zero"。
使用示例:
ghci> :set -XTypeApplications
ghci> values @Foo
K Bar :* K Baz :* Nil
您可以使用现有的 injections
或 apInjs*
函数。
有
apInjs'_NP :: SListI xs => NP f xs -> NP (K (NS f xs)) xs
你必须提供函数参数的乘积,在我们的
一般情况下,每个组件将应用于其中一个
基础数据类型的构造函数。
但是因为我们假设的是枚举类型,none 其中
构造函数有任何参数,我们可以提供空的
到处都是争论的清单!
values :: forall r . IsEnumType r => NP (K r) (Code r)
values =
map_NP
(mapKK (to . SOP))
(apInjs'_NP
(cpure_NP (Proxy @((~) '[])) Nil)
)
我正在使用 generics-sop 库。我想写一个具有以下类型的值:
values :: forall r. IsEnumType r => NP (K r) (Code r)
也就是说,对于其构造函数没有任何参数的求和类型(IsEnumType
) I want to produce an n-ary product (NP
) which contains每个点对应的构造函数值。
例如,对于类型
{-# 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)) _
也许有更简单的方法,但我设法通过使用辅助类型类 POSN
来定义 values
,它基本上对空类型级列表的类型级列表执行归纳:
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
总是Nil
,因为它们对应于每个构造函数的参数,而且从来没有任何参数。
归纳步骤 "adds one" 到列表其余部分的每个总和,在头部添加一个 "zero"。
使用示例:
ghci> :set -XTypeApplications
ghci> values @Foo
K Bar :* K Baz :* Nil
您可以使用现有的 injections
或 apInjs*
函数。
有
apInjs'_NP :: SListI xs => NP f xs -> NP (K (NS f xs)) xs
你必须提供函数参数的乘积,在我们的 一般情况下,每个组件将应用于其中一个 基础数据类型的构造函数。
但是因为我们假设的是枚举类型,none 其中 构造函数有任何参数,我们可以提供空的 到处都是争论的清单!
values :: forall r . IsEnumType r => NP (K r) (Code r)
values =
map_NP
(mapKK (to . SOP))
(apInjs'_NP
(cpure_NP (Proxy @((~) '[])) Nil)
)