标记工会的标记应该是一流的值,并且稍加努力,它们就是。
跳棋警报:
{-# LANGUAGE GADTs, DataKinds, KindSignatures,
TypeFamilies, PolyKinds, FlexibleInstances,
PatternSynonyms
#-}
第一步:定义标签的类型级版本。
data TagType = EmptyTag | SingleTag | PairTag | LotsTag
第二步:为类型级标签的可表示性定义值级见证。 Richard Eisenberg 的 Singletons 库将为您执行此操作。我的意思是这样的:
data Tag :: TagType -> * where
EmptyT :: Tag EmptyTag
SingleT :: Tag SingleTag
PairT :: Tag PairTag
LotsT :: Tag LotsTag
现在我们可以说出我们期望找到与给定标签相关联的内容。
type family Stuff (t :: TagType) :: * where
Stuff EmptyTag = ()
Stuff SingleTag = Int
Stuff PairTag = (Int, Int)
Stuff LotsTag = [Int]
所以我们可以重构你首先想到的类型
data NumCol :: * where
(:&) :: Tag t -> Stuff t -> NumCol
并使用PatternSynonyms 来恢复您想到的行为:
pattern Empty = EmptyT :& ()
pattern Single i = SingleT :& i
pattern Pair i j = PairT :& (i, j)
pattern Lots is = LotsT :& is
所以发生的事情是NumCol 的每个构造函数都变成了一个标签,该标签由它所针对的标签类型索引。也就是说,构造函数标签现在与其他数据分开存在,由一个公共索引同步,确保与标签关联的内容与标签本身匹配。
但我们可以单独讨论标签。
data Ex :: (k -> *) -> * where -- wish I could say newtype here
Witness :: p x -> Ex p
现在,Ex Tag 是“具有类型级别对应物的运行时标签”的类型。它有一个Eq 实例
instance Eq (Ex Tag) where
Witness EmptyT == Witness EmptyT = True
Witness SingleT == Witness SingleT = True
Witness PairT == Witness PairT = True
Witness LotsT == Witness LotsT = True
_ == _ = False
此外,我们可以轻松提取NumCol的标签。
numColTag :: NumCol -> Ex Tag
numColTag (n :& _) = Witness n
这使我们能够匹配您的规格。
filter ((Witness PairT ==) . numColTag) :: [NumCol] -> [NumCol]
这引发了一个问题,即您的规范是否真的是您所需要的。关键是检测到一个标签会让你对这个标签的东西有一个期望。输出类型 [NumCol] 并不能说明您知道自己只有对这一事实。
您如何收紧您的功能类型并仍然交付它?