为了与Idris进行一些练习,我一直试图将各种基本的代数结构表示为接口。我最初组织事物的方式是让给定接口的参数设置为和上面的各种操作,方法/字段是各种公理的证明。例如,我正在考虑定义Group
,如此:
Group (G : Type) (op : G -> G -> G) (e : G) (inv : G -> G) where
assoc : {x,y,z : G} -> (x `op` y) `op z = x `op` (y `op` z)
id_l : {x : G} -> x `op` e = x
id_r : {x : G} -> x `op` e = x
inv_l : {x : G} -> x `op` (inv x) = e
inv_r : {x : G} -> (inv x) `op` x = e
我采用这种方式而不仅仅是制作op
,e
和inv
方法的理由是,在同一组中讨论相同的组是更容易的方法。就像在数学上一样,谈论集合是一个群体是没有意义的;只讨论一个指定操作是一个组的集合才有意义。通过不同地定义操作,相同的组可以对应于两个完全不同的组。另一方面,各种界面法则的证明不会影响群体。虽然法律的居民(证明)可能不同,但它不会导致不同的群体。因此,对于声明多个实现没有用处。
更基本的是,这种方法似乎更好地代表了数学概念。将一个集合作为一个组来讨论这是一个类别错误,所以我的数学家并没有因为使用group操作成为一个接口方法而对断言这一点感到兴奋。
然而,这种方案是不可能的。当我尝试时,它实际上做了类型检查,但是一旦我尝试定义一个实例,它就不会:idris抱怨说,例如:
(+) cannot be a parameter of Algebra.Group
(Implementation arguments must be type or data constructors)
我的问题是:为什么这个限制?我认为这是一个很好的理由,但对于我的生活,我看不到它。就像,我认为Idris崩溃了价值/类型/种类层次结构,因此类型和价值之间没有真正的区别,那么为什么实施特别对待类型呢?为什么数据构造器是专门处理的?这对我来说似乎是随意的。
现在,我可以使用命名实现来实现相同的功能,我想我现在最终会这样做。我想我只是习惯了Haskell,你只能为给定的数据类型提供一个类型类的实例。但它仍然感觉相当随意....特别是,我希望能够将例如半环定义为元组(R,+,*,0,1)
,其中(R,+,0)
是一个幺半群而(R,*,1)
是一个幺半群(加上ditributivity法则)。但是,如果没有上述方案,即使使用命名实现,我也不认为我能够轻松地做到这一点。我只能说R是否是幺半群 - 但对于半环来说,它需要以两种截然不同的方式成为幺半群!我确信有一些样板式类型的同义词或类似的东西(我可能最终会这样做),但我真的不明白为什么这是必要的。
$ idris --version
1.2.0