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

Log*_*ins 4 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]
Run Code Online (Sandbox Code Playgroud)

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

bar : Vect n Int -> Vect (4*n) Int
bar vals = concatMap foo vals
Run Code Online (Sandbox Code Playgroud)

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

concatMap的类型签名是:

Prelude.Foldable.concatMap : Foldable t => Monoid m => (a -> m) -> t a -> m
Run Code Online (Sandbox Code Playgroud)

因此,如果我尝试编译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)
Run Code Online (Sandbox Code Playgroud)

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

Prelude.Algebra.neutral : Monoid a => a
Run Code Online (Sandbox Code Playgroud)

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

instance Monoid (List a) where
    neutral = []
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试使用中性= []为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
Run Code Online (Sandbox Code Playgroud)

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

Bak*_*riu 7

你不能实现一个monoid,这样你就可以使用写下该表达式concatMap.想想签名concatMap:

(Foldable t, Monoid m) => (a -> m) -> t a -> m
Run Code Online (Sandbox Code Playgroud)

请注意,函数参数的返回类型和整个函数的返回类型m必须相同Monoid(a -> m).然而这是不是这样Vect n a.考虑一下表达式:

concatMap foo vals
Run Code Online (Sandbox Code Playgroud)

这里foo有一个类型,你想要a -> Vect 4 a的结果concatMap是类型Vect (4*n) a,其中n是原始向量的长度.但这不适合该concatMap类型,因为您的应用程序concatMap需要类型:

(Foldable t, Monoid m, Monoid m1) => (a -> m) -> t a -> m1
Run Code Online (Sandbox Code Playgroud)

其中结果monoid和函数返回的值可以是不同的类型,同时concatMap强制使用相同的类型.

[a]并且Vect n a是完全不同的,因为[]没有包括在类型长度,这可以让你写一个concatMap函数.实际上,这允许您将Monoid实例[]和连接作为二元运算符.

当您启动长度连接到一个类型这种可能性消失,因为你不能再混用不同的长度,因此Vect n a没有形成串联一个独异.连接运算符a -> b -> c在一般情况下变为类型,特别是Vects 的类型Vect n a -> Vect m a -> Vect (n+m) a,这与列表的类型明显不同:[a] -> [a] ->[a].


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

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

在这种情况下,他的neutral向量必须是长度的n,并且你可以给它的元素唯一合理的值是neutral元素Monoid.基本上你想要neutralVect n areplicate n neutral.

然后,二进制操作Monoid将是元素之间的操作的元素应用.

所以实例看起来像:

instance Monoid a => Monoid (Vect n a) where
    neutral = replicate n neutral

instance Semigroup a => Semigroup (Vect n a) where
    Nil <+> Nil = Nil
    (x :: xs) <+> (y :: ys) = (x <+> y) :: (xs <+> ys)
Run Code Online (Sandbox Code Playgroud)

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