我将研究由 Edward Kmett 在鉴别包中实现的 Fritz Henglein 的广义基数排序技术中的核心数据类型示例。
虽然那里发生了很多事情,但它主要集中在这样的类型上
data Group a = Group (forall b . [(a, b)] -> [[b]])
如果你有一个 type 的值,Group a
你基本上必须有一个等价关系,a
因为如果我给你一个a
s 和b
你完全不知道的类型之间的关联,那么你可以给我 的“分组” b
。
groupId :: Group a -> [a] -> [[a]]
groupId (Group grouper) = grouper . map (\a -> (a, a))
您可以将其视为编写分组实用程序库的核心类型。例如,我们可能想知道如果我们可以Group a
,Group b
然后我们可以Group (a, b)
(稍后会详细介绍)。Henglein 的核心思想是,如果你可以从Group
整数的一些基本 s 开始——我们可以通过基数排序编写非常快速Group Int32
的实现——然后使用组合子将它们扩展到所有类型,那么你将把基数排序推广到代数数据类型。
那么我们如何构建我们的组合器库呢?
好吧,f :: Group a -> Group b -> Group (a, b)
这非常重要,因为它可以让我们创建类似产品的类型组。通常,我们会从Applicative
andliftA2
但是Group
,你会注意到,isContravaiant
不是 a Functor
。
所以我们使用Divisible
divided :: Group a -> Group b -> Group (a, b)
请注意,这是以一种奇怪的方式出现的
divide :: (a -> (b, c)) -> Group b -> Group c -> Group a
因为它具有逆变事物的典型“反向箭头”特征。我们现在可以根据它们divide
的conquer
解释来理解Group
.
Dividea
说,如果我想使用等于b
s 和s的策略来构建一个等于s 的策略c
,我可以对任何类型执行以下操作x
使用你的部分关系并用一个函数和一些元组操作来[(a, x)]
映射它,以获得一个新的关系。f :: a -> (b, c)
[(b, (c, x))]
Use my Group b
to discriminate [(b, (c, x))]
into [[(c, x)]]
Use my Group c
to discriminate each [(c, x)]
into [[x]]
giving me [[[x]]]
Flatten the inner layers to get [[x]]
like we need
instance Divisible Group where
conquer = Group $ return . fmap snd
divide k (Group l) (Group r) = Group $ \xs ->
-- a bit more cleverly done here...
l [ (b, (c, d)) | (a,d) <- xs, let (b, c) = k a] >>= r
We also get interpretations of the more tricky Decidable
refinement of Divisible
class Divisible f => Decidable f where
lose :: (a -> Void) -> f a
choose :: (a -> Either b c) -> f b -> f c -> f a
instance Decidable Group where
lose :: (a -> Void) -> Group a
choose :: (a -> Either b c) -> Group b -> Group c -> Group a
These read as saying that for any type a
of which we can guarantee there are no values (we cannot produce values of Void
by any means, a function a -> Void
is a means of producing Void
given a
, thus we must not be able to produce values of a
by any means either!) then we immediately get a grouping of zero values
lose _ = Group (\_ -> [])
We also can go a similar game as to divide
above except instead of sequencing our use of the input discriminators, we alternate.
Using these techniques we build up a library of "Group
able" things, namely Grouping
class Grouping a where
grouping :: Group a
and note that nearly all the definitions arise from the basic definition atop groupingNat
which uses fast monadic vector manipuations to achieve an efficient radix sort.