在我看来,Num
类型类由一组非常随意的函数组成。有很多类型自然会有+
和*
个操作,但由于存在Num
,abs
,因此signum
的实例存在问题,和fromInteger
。我找不到关于这个课程背后的设计理念的任何讨论,所以我不清楚这里是否有合理的理由,或者这是一个不幸的历史怪异。
我将举例说明我的问题。假设我正在实现一个Matrix
类,其组件为Double
。我显然可以实施+
,*
,-
和negate
。也许fromInteger x
可以为1 {1} Matrix
组成Double
值fromInteger x
。如何处理abs
和signum
不太明显,但我可以想出一些满足规则的东西(来自班级的文档):
abs x * signum x == x
对此观点的反对意见是我的Num
实例未能满足人们对Num
所期望的一些隐含规则。我的*
是一个部分函数(假设Matrix
的大小是运行时参数),但对于Double
和Int
等常见实例则不然。而且它不通勤。无论我为abs
和signum
提出什么都不会满足每个人的期望。
对这一异议的反对意见是我的Matrix
乘法无论如何都将是一个部分函数(并且在Haskell社区中似乎被接受的这种类型),那么为什么它是重要的呢? *
特别是部分功能?如果我的abs
和signum
满足文档中的规则,那么我就完成了我的讨价还价。任何依赖Num
实例的人都是错误的。
类似Matrix
的类型应该是Num
的实例吗?
答案 0 :(得分:3)
不要为非响铃制作Num
个实例。这只是令人困惑。
当然,您通常可以定义某些有用的的实例,但如果它不是完全明显的是什么那么最好只定义一个具有描述性名称的普通函数,或者一些较弱的类实例,具有更好定义的语义。如果有人想在短操作符或多态Num
函数中使用它,他们仍然可以在自己的模块中本地定义(最好使用简单的newtype
包装。
特别是,Num
- 一般(动态大小)矩阵的实例是有问题的,因为当尺寸不匹配时应该发生什么并不明显。你想要概括什么行为?
我认为好的例子是固定二次大小的矩阵(即给定向量空间上的线性内同态)。在这种情况下,乘法显然是组合†,并且数字文字将被视为恒定对角矩阵,因此1
实际上是乘法同一性。就像你在数学语境中写的一样。
但这与你任意选择数字文字大小为1×1的想法不相符!人们希望2 * m
可以工作,但它会崩溃。那么,更好的崩溃比给出意想不到的结果不幸的是,想出一种以合适的方式定义乘法的聪明方法很诱人。例如,我们可以对较小的矩阵进行块对角复制,直到它足够大,也许只在1×1的情况下这样做......好吧,Matlab做了这种特殊的东西,但请!我们不要将such a horrible language作为一个好主意的模型。
如果你的某些东西显然是一个附加组,实际上是向量空间,那么就把它变成VectorSpace
!如果你也有乘法,但它是偏的,那么最好只将它定义为普通函数。
如果您喜欢,可以为精细交错的numeric-prelude类定义实例。就个人而言(虽然我喜欢这个项目的想法)但我还是不愿意在任何地方使用它,因为理解层次结构是一种非常有用的方法。
† 或者是?麻烦已经从这里开始,我认为hmatrix实际上在矩阵上实现了*
作为逐元素乘法。这比Matlab更可怕!