我有以下代码实现静态大小的向量实现为编译精确的列表:
{-# 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
答案 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
(比我更精通这种类型级编程的人可能会提供更具体的建议)。