使用静态大小列表

时间:2017-03-21 06:59:13

标签: haskell type-level-computation

我有以下代码实现静态大小的向量实现为编译精确的列表:

{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE IncoherentInstances #-}

import GHC.TypeLits

infixr 5 :.
data Vector :: Nat -> * -> * where
  (:-:):: Vector 0 a
  (:.) :: a -> Vector (n-1) a -> Vector n a

deriving instance Show a => Show (Vector n a)

instance Functor (Vector n) where
  fmap f (:-:) = (:-:)
  fmap f (x :. xs) = f x :. fmap f xs

type x > y = CmpNat x y ~ 'GT

instance Applicative (Vector 0) where
  pure f    = (:-:)
  (<*>) _ _ = (:-:)

instance (Applicative (Vector (n-1)), n > 0 ) => Applicative (Vector n) where
  pure (f::a)          = f :. (pure f :: Vector (n-1) a)
  (f:.fs) <*> (x:.xs) = f x :. (fs <*> xs)

但是,当我尝试为这些向量定义元素和时:

(<+>) :: Num a => Vector n a -> Vector n a -> Vector n a
v1 <+> v2 = (+) <$> v1 <*> v2

我收到编译错误

Reduction stack overflow; size = 201
When simplifying the following type: Applicative (Vector s0)
(...)
In the expression: (+) <$> v1 <*> v2

如果我可以在ghci中键入右手表达式并且它工作正常,以及如何在将来避免它,会导致此错误的原因是什么?

我正在使用ghc 8.0.1和natnormalize 0.5.2

1 个答案:

答案 0 :(得分:2)

  
    

猜测:您是否还需要将[约束]放在(<+>)的签名中?

  
     

[您的] Applicative约束建议有效。您能详细说明为什么我需要指定(Vector n)是Applicative的实例,因为所有向量长度都应该被实例化为应用程序吗?

简而言之,GHC无法以归纳的方式得出这一结论。相反,类型检查器在被告知您需要Applicative (Vector n)时所做的是退后一步并查看Applicative (Vector (n - 1)),以查看是否满足(Applicative (Vector (n - 1)), n > 0) => Applicative n。由于您没有使用具体的n,因此永远不会终止(这与您的测试与特定值的区别)。既然如此,您需要提供额外的信息。顺便说一下,你会得到相同的&#34;减少堆栈溢出&#34;如果你尝试更平淡的东西会出错,例如:

GHCi> :t undefined == (undefined :: [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[a]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]])

由于Eq [a]需要Eq a,如果a ~ [b],则还必须检查Eq b,依此类推。

另一方面,据我所知,以KnownNat而不是像Applicative那样编写这种约束更为常见。 {{1}},以便最大限度地减少必须在函数签名中传播的约束数量。参看例如the source of CLaSH.Sized.Vector(比我更精通这种类型级编程的人可能会提供更具体的建议)。