在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?
答案 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]
这就是说,您报告的错误是由于在为Monoid
类Vect 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的伪代码来概括这些概念。