是否必须为所有参数定义半组的运算符?

时间:2018-11-25 09:51:46

标签: haskell

下面的示例是否正确定义了半群?

instance Semigroup (CurrencyAmount Fixed2) where
  (<>) (CurrencyAmount a c) (CurrencyAmount _ EMPTY) = CurrencyAmount a c
  (<>) (CurrencyAmount _ EMPTY) (CurrencyAmount a c) = CurrencyAmount a c
  (<>) (CurrencyAmount a c1) (CurrencyAmount b c2) | c1 == c2 = CurrencyAmount (a + b) c1
  (<>) _ _ = error "currency mismatch"

2 个答案:

答案 0 :(得分:3)

我想这是一个语义问题。半群(A,<>)的形式和经典数学定义确实要求<>是函数<>:A×A→A,因此是总函数。但是您设置的A是什么?您需要为正在使用的(Haskell)类型和函数建立一些(幼稚的)基于理论的语义。

  • 如果类型的表示是包含作为元素的载体集,则Haskell函数将被解释为总数学函数。在这种情况下,您只需要确保所有元素(包括)都符合必需的法律即可。而在您的情况下,它们确实如此。

  • 如果相反,您考虑集合(不包含)和部分函数的模型,那么要匹配Semigroup的经典形式数学定义,您需要确保{{1 }}是该设置的总和。在您的情况下不是。

我不知道是否有固定的约定。第二,更严格的解释似乎是可取的。但是首先是明智的概括。

那就是说,您是否考虑过使用GADT在类型级别上区分不同货币的可能性?

答案 1 :(得分:2)

我认为这无效。

Haskell中的大多数合法类型类别(例如SemigroupMonoidFunctorMonad等)都是从数学(例如类别理论)得出的定律

数学是基于公理的;也就是说,有关数字,几何图形和类似图形的基本结构的断言。根据定义,公理无法得到证明,但是您通常会接受它们,因为它们具有直观的意义(例如Peano公理)。从某种意义上说,数学是建立在直觉的基础上的。

之所以使用上述类型类是合法的,是因为法律确保实例的行为与您期望的一样。

在Haskell中,非常强调能够理解代码,主要是通过仅查看函数的类型签名。作为读者,您对执行此操作充满信心的原因之一是,只要涉及到合法的类型类,您就会知道它的行为会令人惊讶。

大多数数学函数都是 total (存在例外情况,例如被零除,但例外情况应来自数学本身)。鉴于上述情况,我认为有一个强烈的隐含期望,即期望从数学中得出其定律的类型类是总数。

以上实例并非全部,这意味着对于某些输入,它将以令人惊讶的方式表现。我认为它不是有效的Semigroup实例。