下面的示例是否正确定义了半群?
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"
答案 0 :(得分:3)
我想这是一个语义问题。半群(A,<>)
的形式和经典数学定义确实要求<>
是函数<>:A×A→A
,因此是总函数。但是您设置的A
是什么?您需要为正在使用的(Haskell)类型和函数建立一些(幼稚的)基于理论的语义。
如果类型的表示是包含⊥
作为元素的载体集,则Haskell函数将被解释为总数学函数。在这种情况下,您只需要确保所有元素(包括⊥
)都符合必需的法律即可。而在您的情况下,它们确实如此。
如果相反,您考虑集合(不包含⊥
)和部分函数的模型,那么要匹配Semigroup
的经典形式数学定义,您需要确保{{1 }}是该设置的总和。在您的情况下不是。
我不知道是否有固定的约定。第二,更严格的解释似乎是可取的。但是首先是明智的概括。
那就是说,您是否考虑过使用GADT在类型级别上区分不同货币的可能性?
答案 1 :(得分:2)
我认为这无效。
Haskell中的大多数合法类型类别(例如Semigroup
,Monoid
,Functor
,Monad
等)都是从数学(例如类别理论)得出的定律
数学是基于公理的;也就是说,有关数字,几何图形和类似图形的基本结构的断言。根据定义,公理无法得到证明,但是您通常会接受它们,因为它们具有直观的意义(例如Peano公理)。从某种意义上说,数学是建立在直觉的基础上的。
之所以使用上述类型类是合法的,是因为法律确保实例的行为与您期望的一样。
在Haskell中,非常强调能够理解代码,主要是通过仅查看函数的类型签名。作为读者,您对执行此操作充满信心的原因之一是,只要涉及到合法的类型类,您就会知道它的行为会令人惊讶。
大多数数学函数都是 total (存在例外情况,例如被零除,但例外情况应来自数学本身)。鉴于上述情况,我认为有一个强烈的隐含期望,即期望从数学中得出其定律的类型类是总数。
以上实例并非全部,这意味着对于某些输入,它将以令人惊讶的方式表现。我认为它不是有效的Semigroup
实例。