Haskell :: 如何创建任意长度的向量?

Арт*_*802 7 polymorphism haskell vector dependent-type type-level-computation

想要在 Haskell 中实现类型安全的矩阵乘法。定义如下:

{-# LANGUAGE TypeFamilies, DataKinds, GADTs  #-}
module Vector where

data Nat = Succ Nat | Zero

data Vector (n :: Nat) a where
    Nil :: Vector 'Zero a
    (:::) :: a -> Vector n a -> Vector (Succ n) a
type Matrix n m a = Vector m (Vector n a)

instance Foldable (Vector n) where
    foldr f b (a ::: as) = f a (foldr f b as)
    foldr _ b Nil = b

instance Functor (Vector n) where
    fmap f (x ::: xs) = f x ::: fmap f xs
    fmap _ Nil = Nil

zipV :: (a -> b -> c) -> Vector n a -> Vector n b -> Vector n c
zipV f (a ::: as) (b ::: bs) = f a b ::: zipV f as bs
zipV f Nil Nil = Nil
Run Code Online (Sandbox Code Playgroud)

最终需要实施

transpose :: Matrix n m a -> Matrix m n a
Run Code Online (Sandbox Code Playgroud)

但我在 Haskell 中能做的最好的事情是:

transpose :: Matrix n (Succ m) a -> Matrix (Succ m) n a
transpose (h ::: rest@(_ ::: _)) = zipV (:::) h (transpose rest)
transpose (h ::: Nil) = fmap (::: Nil) h
Run Code Online (Sandbox Code Playgroud)

这仅限于 m > 0 因为我无法实现

nils :: {n :: Nat} -> Vector n (Vector Zero a)
Run Code Online (Sandbox Code Playgroud)

切换到 Idris 只是为了练习并且做得更好:

matrix : Nat -> Nat -> Type -> Type
matrix n m a = Vector n (Vector m a)

nils : {n: Nat} -> Vector n (Vector Z a)
nils {n = Z}   = Nil
nils {n = S k} = Nil ::: nils

transpose : matrix n m a -> matrix m n a
transpose (h ::: rest) = zipV (:::) h (transpose rest)
transpose Nil = nils
Run Code Online (Sandbox Code Playgroud)

我有实现 nils 的冲动,但是 Haskell 中的类型级编程非常尴尬。我还必须在 Haskell 中对 rest@(_ ::: _) 进行模式匹配,但在 Idris 中却没有。我怎样才能实现“nils”?

lef*_*out 6

这本质上就是单例的用途。这是类型类的值级见证,它使您可以访问每个数字实际上都可以用标准形式描述的(概念上冗余的)信息。一个最小的实现:

\n
data NatSing n where\n  ZeroSing :: NatSing Zero\n  SuccSing :: KnownNat n => NatSing (Succ n)\n\nclass KnownNat n where\n  natSing :: NatSing n\ninstance KnownNat Zero where natSing = ZeroSing\ninstance KnownNat n => KnownNat (Succ n) where natSing = SuccSing\n
Run Code Online (Sandbox Code Playgroud)\n

现在可以写

\n
{-# LANGUAGE ScopedTypeVariables, UnicodeSyntax, TypeApplications #-}\nnils :: \xe2\x88\x80 n a . KnownNat n => Vector n (Vector Zero a)\nnils = case natSing @n of\n  ZeroSing ->     Nil\n  SuccSing -> Nil ::: nils\n
Run Code Online (Sandbox Code Playgroud)\n