如何使Vect n Int成为Monoid的一个实例

时间:2014-10-04 13:41:18

标签: haskell types functional-programming dependent-type idris

在Idris中,Vect n a是一种数据类型,表示包含类型a的项目的n长度的向量。想象一下,我有一个功能:

foo : Int -> Vect 4 Int
foo n = [n-1, n, n+1, n*4]

函数的主体并不重要,它可以是返回4 Ints向量的任何东西。现在,我想将此函数与concatMap一起使用,如下所示:

bar : Vect n Int -> Vect (4*n) Int
bar vals = concatMap foo vals

Bar是一个函数,它采用长度为n的Int向量并返回长度为4 * n的向量。

concatMap的类型签名是:

Prelude.Foldable.concatMap : Foldable t => Monoid m => (a -> m) -> t a -> m

因此,如果我尝试编译bar,我会收到错误:

 When elaborating right hand side of bar:
     Can't resolve type class Monoid (Vect (plus n (plus n (plus n (plus n 0)))) Int)

这意味着Vect n Int不是monoid的实例。为了使它成为monoid的一个实例,我需要实现:

Prelude.Algebra.neutral : Monoid a => a

不幸的是,我不知道该怎么做。 List实现了monoid,如下所示:

instance Monoid (List a) where
    neutral = []

但是如果我尝试用Vect n Int的中性= []实现monoid,我会收到错误:

 When elaborating right hand side of Prelude.Algebra.Vect n Int instance of Prelude.Algebra.Monoid, method neutral:
 |   Can't unify
 |           Vect 0 Int
 |   with
 |           Vect n Int
 |   
 |   Specifically:
 |           Can't unify
 |                   0
 |           with
 |                   n

所以我想知道,我将如何为Vect实现monoid?

1 个答案:

答案 0 :(得分:7)

您无法实现monoid,因此您可以使用concatMap写下该表达式。想想concatMap的签名:

(Foldable t, Monoid m) => (a -> m) -> t a -> m

请注意,m必须与函数参数Monoid的返回类型和整个函数的返回类型中的相同(a -> m) 。但 Vect n a的情况并非如此。考虑表达式:

concatMap foo vals

此处foo的类型为a -> Vect 4 a,您希望concatMap的结果类型为Vect (4*n) a,其中n的长度为concatMap原始矢量。但是这不适合concatMap类型,因为您的(Foldable t, Monoid m, Monoid m1) => (a -> m) -> t a -> m1 应用程序需要类似的类型:

concatMap

其中结果monoid和函数返回的值可以是不同的类型,而[a]强制使用相同的类型。

Vect n a[]完全不同,因为concatMap 包含类型中的长度,这样您就可以编写{{1}功能。实际上,这允许您为Monoid创建[]实例并将其连接为二元运算符。

当您开始将长度附加到某个类型时,这种可能性会消失,因为您不能再混合不同的长度,因此Vect n a 不会形成用于连接的幺半群。 在一般情况下,连接运算符的类型为a -> b -> c,特别是Vect的类型为Vect n a -> Vect m a -> Vect (n+m) a,这与列表的类型明显不同:[a] -> [a] ->[a]


这就是说,您报告的错误是由于在为MonoidVect n a编写实例时,neutral的值必须是Vect n a类型但[]的类型为Vect 0 a

但是,您可以在Monoid上为Vect n a类型类创建不同的实例。 如果这样的矢量的元素是幺半群,那么你可以创建这种矢量的幺半群。

在这种情况下,他neutral向量的长度必须为n,并且您可以为其元素提供的唯一合理值是元素neutral的{​​{1}} 。 基本上,您希望Monoid的{​​{1}}为neutral

Vect n a的二进制操作将是元素之间操作的元素应用。

所以实例看起来像是:

replicate n neutral

不幸的是,我不是Idris用户/程序员,因此我无法确切地告诉您如何正确编写此类代码。以上只是一个类似Haskell的伪代码来概括这些概念。