在类型安全向量

时间:2018-04-18 03:59:54

标签: haskell data-kinds

我正在尝试写下Category (有限维自由)向量空间,但我似乎无法说服GHC任何给定长度的索引向量是{ {1}}。

这就是我所拥有的:

Applicative

向量是具有长度参数

的列表
{-# LANGUAGE DataKinds, PolyKinds, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, GADTs, DeriveTraversable, StandaloneDeriving #-}

-- | Quick(slow) and dirty typesafe vectors
module Vector where
import Control.Category

要获得该类别,我们需要矩阵乘法。明显的实施使得指数与我们通常想要的相比有点落后。

data Natural = Z | S Natural
data Vec (n :: Natural) a where
  VNil :: Vec Z a
  VCons :: a -> Vec n a -> Vec (S n) a
deriving instance Functor (Vec n)
deriving instance Foldable (Vec n)
deriving instance Traversable (Vec n)

修改

使用@ Probie的help,我设法解决了早期的问题,这足以为vmult :: Num a => Vec i (Vec k a) -> Vec j (Vec k a) -> Vec j (Vec i a) -- ^ ^ ^ ^ ^ ^ vmult _ VNil = VNil vmult xs (VCons y ys) = VCons (dotProduct y <$> xs) $ vmult xs ys dotProduct :: Num a => Vec n a -> Vec n a -> a dotProduct VNil VNil = 0 dotProduct (VCons x xs) (VCons y ys) = x * y + dotProduct xs ys s

定义一个实例
Semigroupoid

但在定义data KNat n where KZ :: KNat Z KS :: Finite n => KNat n -> KNat (S n) class Finite (a :: Natural) where toFNat :: proxy a -> KNat a instance Finite Z where toFNat _ = KZ instance Finite n => Finite (S n) where toFNat _= KS (toFNat (Proxy :: Proxy n)) instance Finite n => Applicative (Vec n) where pure :: forall a. a -> Vec n a pure x = go (toFNat (Proxy :: Proxy n)) where go :: forall (m :: Natural). KNat m -> Vec m a go KZ = VNil go (KS n) = VCons x $ go n (<*>) :: forall a b. Vec n (a -> b) -> Vec n a -> Vec n b nfs <*> nxs = go (toFNat (Proxy :: Proxy n)) nfs nxs where go :: forall (m :: Natural). KNat m -> Vec m (a -> b) -> Vec m a -> Vec m b go KZ VNil VNil = VNil go (KS n) (VCons f fs) (VCons x xs) = VCons (f x) (go n fs xs) data Matrix a i j where Matrix :: (Finite i, Finite j) => Vec i (Vec j a) -> Matrix a i j instance Num a => Semigroupoid (Matrix a) where Matrix x `o` Matrix y = Matrix (vmult (sequenceA x) y) 时遇到了类似的问题:

Category.id

我现在需要一个instance Num a => Category (Matrix a) where (.) = o id :: forall (n :: Natural). Matrix a n n id = Matrix (go (toFNat (Proxy :: Proxy n))) where go :: forall (m :: Natural). (KNat m) -> Vec m (Vec m a) go KZ = VNil go (KS n) = VCons (VCons 1 (pure 0)) (VCons 0 <$> go n) ,而不需要从空中召唤Applicative (Vec n)

Finite n

似乎没有办法解决这方面的问题。

结束编辑

对于上下文,这就是我之前所拥有的,src/Vector.hs:59:8: error: • Could not deduce (Finite n) arising from a use of ‘Matrix’ from the context: Num a bound by the instance declaration at src/Vector.hs:56:10-37 Possible fix: add (Finite n) to the context of the type signature for: Control.Category.id :: forall (n :: Natural). Matrix a n n • In the expression: Matrix (go (toFNat (Proxy :: Proxy n))) In an equation for ‘Control.Category.id’: Control.Category.id = Matrix (go (toFNat (Proxy :: Proxy n))) where go :: forall (m :: Natural). (KNat m) -> Vec m (Vec m a) go KZ = VNil go (KS n) = VCons (VCons 1 (pure 0)) (VCons 0 <$> go n) In the instance declaration for ‘Category (Matrix a)’ | 59 | id = Matrix (go (toFNat (Proxy :: Proxy n))) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 是归纳的Vec n

Applicative

要获取类别实例,我们需要重新排列索引后面的instance Applicative (Vec Z) where pure _ = VNil _ <*> _ = VNil instance Applicative (Vec n) => Applicative (Vec (S n)) where pure a = VCons a $ pure a VCons f fs <*> VCons x xs = VCons (f x) (fs <*> xs) ...

a

但是如果没有重新安排其中一个条款,就没有办法重新安排指数本身......

data Matrix a i j where
  Matrix :: Vec i (Vec j a) -> Matrix a i j

然而:

instance Num a => Category (Matrix a) where
  Matrix x . Matrix y = Matrix $ (vmult (sequenceA x) y)
--                                       ^^^^^^^^^

2 个答案:

答案 0 :(得分:2)

我还没有在Haskell中使用依赖类型来玩太多,但这类似于应该可行的东西。我设法得到了一些可以编译的东西,但我认为这有更好的方法......

诀窍是能够生成一些可以递归的东西,其中包含足够的类型信息而不需要已经有Vector。这允许我们将两个Applicative实例折叠到一个Applicative实例中(不幸的是添加一个约束Finite,希望以后不会导致问题)

{-# LANGUAGE DataKinds, PolyKinds, MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, GADTs, DeriveTraversable, StandaloneDeriving, ScopedTypeVariables #-}

module Vector where
import Control.Category
import Data.Proxy

data Natural = Z | S Natural

data SNat n where
  SZ :: SNat Z
  SS :: Finite n => SNat n -> SNat (S n)

class Finite (a :: Natural) where
  toSNat :: proxy a -> SNat a

instance Finite Z where
  toSNat _ = SZ
instance (Finite a) => Finite (S a) where
  toSNat _ = SS (toSNat (Proxy :: Proxy a))

data Vec (n :: Natural) a where
  VNil :: Vec Z a
  VCons :: (Finite n) => a -> Vec n a -> Vec (S n) a

deriving instance Functor (Vec n)
deriving instance Foldable (Vec n)
deriving instance Traversable (Vec n)

instance (Finite n) => Applicative (Vec n) where
  pure (a :: a) = go (toSNat (Proxy :: Proxy n))
    where go :: forall (x :: Natural) . SNat x -> Vec x a
          go SZ = VNil
          go (SS m) = VCons a (go m)
  (fv :: Vec n (a -> b)) <*> (xv :: Vec n a) = go (toSNat (Proxy :: Proxy n)) fv xv
    where go :: forall (x :: Natural) . SNat x -> Vec x (a -> b) -> Vec x a -> Vec x b
          go SZ VNil VNil = VNil
          go (SS m) (VCons f fs) (VCons x xs) = VCons (f x) (go m fs xs)

vmult :: Num a => Vec i (Vec k a) -> Vec j (Vec k a) -> Vec j (Vec i a)
vmult _ VNil = VNil
vmult xs (VCons y ys) = VCons (dotProduct y <$> xs) $ vmult xs ys

dotProduct :: Num a => Vec n a -> Vec n a -> a
dotProduct VNil VNil = 0
dotProduct (VCons x xs) (VCons y ys) = x * y + dotProduct xs ys

data Matrix a i j where
  Matrix :: (Finite i, Finite j) => Vec i (Vec j a) -> Matrix a i j

instance Num a => Category (Matrix a) where
  Matrix x . Matrix y = Matrix $ (vmult (sequenceA x) y)

编辑: Matrix不是一个类别。没有身份 - 我们需要forall (n :: Natural) . Matrix a n n

不幸的是,Haskell中的所有类型都由Any居住,所以我们需要能够拥有Matrix a Any Any,但我们只能有矩阵尺寸为&#34; true&#34; Natural s,所以我们能做的最好的事情是forall (n :: Natural) . Finite n => Matrix a n n 事实证明我错了,实际上可以做到

答案 1 :(得分:1)

经过恐吓(以及火车上几个小时)的一些证据后,我想出了这个。

如果你将Finite的证据推迟到最后,你可以将任何东西都加倍......我们想要写的术语是不可能的。

vdiag :: forall a i j. a -> a -> a -> KNat i -> KNat j -> Vec i (Vec j a)
vdiag u d l = go
  where
    go :: forall i' j'. KNat i' -> KNat j' -> Vec i' (Vec j' a)
    go (KS i) (KS j) =
      VCons (VCons d  $  vpure u j)
            (VCons l <$> go i j)
    go KZ _ = VNil
    go (KS i) KZ = vpure VNil (KS i)

vpure :: a -> KNat m -> Vec m a
vpure x KZ = VNil
vpure x (KS n) = VCons x $ vpure x n

但是,当我们确实需要将i用于j时,我们不知道Category.idMatrix a是什么(除了他们是相等的)。我们CPS他们!给出data Matrix a i j where DiagonalMatrix :: (Finite i => KNat i -> Vec i (Vec i a)) -> Matrix a i i -- ^^^^^^^^^ ^ Matrix :: (Finite i, Finite j) => Vec i (Vec j a) -> Matrix a i j 的额外构造函数,其具有Rank 2约束。

k

任何时候我们碰巧需要增加这些东西,我们可以使用这样一个事实:我们知道另一个词的内部索引instance Num a => Semigroupoid (Matrix a) where o :: forall a i j k. Num a => Matrix a k j -> Matrix a i k -> Matrix a i j Matrix x `o` Matrix y = Matrix (vmult (sequenceA x ) y) DiagonalMatrix fx `o` Matrix y = Matrix (vmult (sequenceA (fx (toFNat (Proxy :: Proxy k)))) y) Matrix x `o` DiagonalMatrix fy = Matrix (vmult (sequenceA x ) (fy (toFNat (Proxy :: Proxy k)))) 是乘以的:

  DiagonalMatrix fx `o` DiagonalMatrix fy = DiagonalMatrix $ 
      \i -> vmult (sequenceA (fx i)) (fy i)

即,除非他们两个对角线。在任何情况下,它们都是相同的,所以我们现在可以使用我们后来获得的索引:

Matrix

正是因为这一步,才有必要将CPS'id限制为只有方形的vdiag。起初我一直尝试CPS,但这要求最终用户记住所有中间索引并证明它们,而不仅仅是结果的索引。虽然我确信这可以起作用,至少对于一个类别来说,它是不必要和严苛的。

无论如何,我们现在已经完成了,instance Num a => Category (Matrix a) where (.) = o id = DiagonalMatrix $ \i -> vdiag 0 1 0 i i 是CPS Matrix a

unMatrix :: (Finite i, Finite j) => Matrix a i j -> Vec i (Vec j a)
unMatrix (Matrix x) = x
unMatrix (DiagonalMatrix fx) = fx (toFNat (Proxy))

type Zero = Z
type One = S Z
type Two = S One
type Three = S Two
type Four = S Three

f :: Vec Two (Vec Three Int)
f = VCons (VCons 1 $ VCons 2 $ VCons 3 VNil)
  $ VCons (VCons 4 $ VCons 5 $ VCons 6 VNil)
  $ VNil

g :: Vec Four (Vec Two Int)
g = VCons (VCons 1 $ VCons 2 VNil)
  $ VCons (VCons 3 $ VCons 4 VNil)
  $ VCons (VCons 5 $ VCons 6 VNil)
  $ VCons (VCons 7 $ VCons 8 VNil)
  $ VNil

fg = unMatrix $ Matrix f . id . Matrix g
--                         ^^

当然,从> fg VCons (VCons 9 (VCons 12 (VCons 15 VNil))) (VCons (VCons 19 (VCons 26 (VCons 33 VNil))) (VCons (VCons 29 (VCons 40 (VCons 51 VNil))) (VCons (VCons 39 (VCons 54 (VCons 69 VNil))) VNil))) 中提取行和列要求您知道您要求的矩阵有多大。

userChoice

但这并不是太大的义务。

compare()

为了完整性,这是整个事情:https://gist.github.com/danbornside/f44907fe0afef17d5b1ce93dd36ce84d