给定类型列表
{-# LANGUAGE DataKinds #-}
type MyTypes = '[String, Int, Char]
以及类似于以下的类型类:
class MyClass a where
describe :: Proxy a -> String
instance MyClass String where
describe _ = "a String is a String"
instance MyClass Int where
describe _ = "an Int represents a number"
instance MyClass Char where
describe _ = "a Char represents a single character"
如何迭代
MyTypes
并在列表中的每种类型上调用 describe
(假设列表中的每种类型都有 MyClass
的实例)?
我想我想要像
fmap
或 traverse
这样的东西在类型级别和值级别之间建立一座桥梁。["a String is a String", "an Int represents a number", "a Char represents a single character"]
sop-core包提供了有用的函数来处理类型级列表和由它们构造或索引的值。特别是 Data.SOP.NP
import Data.SOP ( K(..) )
import Data.SOP.NP ( NP, collapse_NP, cpure_NP )
首先,我们使用 来获得齐次(因为由常数函子 K
参数化)包含 describe
值的 n 元乘积:
descriptions :: NP (K String) MyTypes
descriptions = cpure_NP (Proxy @MyClass) d
where
-- The type signature here is to be able to refer to `a` in the Proxy.
-- Because we are using a constant functor, we don't really have an `a` value,
-- only a String.
d :: forall a. MyClass a => K String a
d = K (describe (Proxy @a))
c-
中的
cpure_NP
是一种符号约定。它的意思是“函数假设某个约束对于类型级列表的所有元素都成立,并且我们通过
Proxy
提供该约束”。因为产品是同质的,所以我们可以使用 将其转换为普通列表:
descriptionList :: [String]
descriptionList =
collapse_NP descriptions
describe
的列表。
import Data.Kind (Type)
class MapMyClass (as :: [Type]) where
mapDescribe :: Proxy as -> [String]
instance MapMyClass '[] where
mapDescribe _ = []
instance (MyClass a, MapMyClass as) => MapMyClass (a ': as) where
mapDescribe as = describe (headProxy as) : mapDescribe (tailProxy as)
headProxy :: Proxy (a ': as) -> Proxy a
headProxy _ = Proxy
tailProxy :: Proxy (a ': as) -> Proxy as
tailProxy _ = Proxy