假设我有一个函数,可以处理编译时已知大小的向量(这些由包提供vector-sized):
{-# LANGUAGE DataKinds, GADTs #-}
module Test where
import Data.Vector.Sized
-- Processes vectors known at compile time to have size 4.
processVector :: Vector 4 Int -> String
processVector = undefined
Run Code Online (Sandbox Code Playgroud)
很好,但是如果我不想处理整数向量,而是向量向量怎么办?
-- Same thing but has subvectors of size 3.
processVector2 :: Vector 4 (Vector 3 Int) -> String
processVector2 = undefined
Run Code Online (Sandbox Code Playgroud)
很好,但是每个子向量都有固定的大小。我想要一个函数,其中子向量可以具有不同的大小,但在编译时仍然已知。
我们可以通过存在量化来做到这一点:
data InnerVector = forall n. InnerVector (Vector n Int)
processVector3 :: Vector 4 InnerVector -> String
processVector3 = undefined
Run Code Online (Sandbox Code Playgroud)
很好,但是如果我想返回的不是字符串而是相同维度的向量怎么办?
processVector4 :: Vector 4 InnerVector -> Vector 4 InnerVector
processVector4 = undefined
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为第二个向量可能具有与输入子向量不同大小的子向量!我希望它们在编译时是相同的。(因此索引 0 处的子向量具有相同的大小,索引 1 处的子向量具有相同的大小,依此类推。)
这有可能实现吗?如果没有,您是否知道(或者您可以创建)一种可以实现这一点的数据结构吗?
我避免使用元组是因为:
Int在示例中)。通过使用存在量化,您可以有效地隐藏内部向量的大小。但是,如果您想编写带有类型的代码来表明您正在保留这些大小,那么您不希望它们被隐藏。相反,你希望你的类型能够大声而清晰地表达它们。
因此,让我们定义一些广播这些内部尺寸的类型。本质上,您需要“向量的向量”类型是一种异构列表,它将这些列表的元素限制为向量。当然,有一些库可以帮助您组合这样的类型,但在这里我们将推出自己的库。只是因为这样做更有趣。
让我们从启用一些语言扩展开始,然后为内部向量及其大小编写一些类型:
{-# LANGUAGE DataKinds, GADTs, InstanceSigs, KindSignatures, TypeOperators #-}
data Nat = Zero | Succ Nat
data Vector :: Nat -> * -> * where
VNil :: Vector Zero a
VCons :: a -> Vector n a -> Vector (Succ n) a
instance Functor (Vector n) where
fmap f VNil = VNil
fmap f (VCons x xs) = VCons (f x) (fmap f xs)
Run Code Online (Sandbox Code Playgroud)
接下来是我们的“锯齿状”矩阵类型(即可变大小向量的向量)。如前所述,这只是一种特定类型的异构列表:
data JaggedMatrix :: [Nat] -> * -> * where
MNil :: JaggedMatrix '[] a
MCons :: Vector n a -> JaggedMatrix ns a -> JaggedMatrix (n : ns) a
Run Code Online (Sandbox Code Playgroud)
就在那里,就是这样。锯齿状矩阵的类型由包含内部向量大小的列表索引。外部尺寸未在类型中说明,但可以简单地从内部尺寸列表的长度导出。
让我们将其付诸实践并编写一个维度保留函数。这是一个明显的例子:
instance Functor (JaggedMatrix ns) where
fmap :: (a -> b) -> JaggedMatrix ns a -> JaggedMatrix ns b
fmap f MNil = MNil
fmap f (MCons xs xss) = MCons (fmap f xs) (fmap f xss)
Run Code Online (Sandbox Code Playgroud)