我正在编写一个函数,我想在Vectors上使用它。换句话说,我有类似的东西:
import qualified Data.Vector.Generic as G
foo :: (G.Vector v a, G.Vector v b, G.Vector v c, G.Vector v d)
=> v a -> v b -> v c -> v d
这样用户可以选择使用Unboxed
vs Storable
矢量等。
但是如果我需要在v
中放置中间值,我会得到Vector限制的组合爆炸,如:
foo :: (G.Vector v a, G.Vector v b, G.Vector v c, G.Vector v d,
G.Vector v (a, b), G.Vector v (a, c), G.Vector v (c, b))
=> v a -> v b -> v c -> v d
如何管理这种详细程度?有没有办法要么
1)GHC隐式生成约束
2)以某种方式将约束重构为一个公共类
答案 0 :(得分:6)
类型系列可能很方便:
{-# LANGUAGE TypeFamilies, KindSignatures, DataKinds, TypeOperators, ConstraintKinds #-}
import qualified Data.Vector.Generic as G
import GHC.Prim
type family Vectors v (a :: [*]) :: Constraint where
Vectors v '[] = ()
Vectors v (a ': as) = (G.Vector v a, Vectors v as)
这将创建一个类型级别函数,该函数获取类型列表并为具有这些类型的泛型Vector
生成适当的约束。您可以像这样使用它:
foo :: (Vectors v [a, b, c, d, (a, b), (a, c), (c, b)]) => v a -> v b -> v c -> v d
这不是很理想,但确实会在一定程度上减少代码。
由于具有更多类型级别技巧的对类型,您可以更好地处理这种约束的爆炸:
type family Combinations v (a :: [*]) :: Constraint where
Combinations v '[] = ()
Combinations v (a ': as) = (G.Vector v a, CombComponent v a as, Combinations v as)
type family CombComponent v (a :: *) (bs :: [*]) :: Constraint where
CombComponent v a '[] = ()
CombComponent v a (b ': bs) = (G.Vector v (a, b), G.Vector v (b, a), CombComponent v a bs)
它有点复杂,但现在我们可以像这样编写第二个foo
签名:
foo :: (Combinations v [a, b, c, d]) => v a -> v b -> v c -> v d
这也可以进一步推广以允许签名如下:
foo :: (Combinations (G.Vector v) [a, b, c, d]) => v a -> v b -> v c -> v d