看起来在 Haskell 中我们可以使用类似于超类约束的实例声明。Semigroup我将使用 Prelude 中的和给出一个示例Monoid。这是 的简化定义Semigroup:
class Semigroup a where
(<>) :: a -> a -> a
Run Code Online (Sandbox Code Playgroud)
这是 Prelude 定义的简化版本,Monoid其中使用超类约束:
class Semigroup a => Monoid a where
mempty :: a
mappend :: a -> a -> a
mappend = (<>)
Run Code Online (Sandbox Code Playgroud)
如果我没有记错的话,超类约束似乎可以被实例声明替换,如下所示:
class Monoid a where
mempty :: a
mappend :: a -> a -> a
instance Monoid a => Semigroup a where
(<>) = mappend
Run Code Online (Sandbox Code Playgroud)
当 Haskell 似乎可以对实例声明(带有约束)做同样的事情时,是否有理由拥有超类约束?应该优先选择哪一个?是否尽可能限制超类?
我将在这里使用 C++ 作为示例来展示我所追求的内容。对于复数算术,它具有复数类型和虚数类型:
https://en.cppreference.com/w/c/language/arithmetic_types#Imaginary_floating_types
这些例如具有这样的属性:将两个数字与双虚数类型相乘将具有双精度类型。这与使用实部为 0.0 的复数几乎但不完全相同,但又不完全相同。虚数类型不会显式存储实部,这会自动消除不需要的计算和 0.0 的存储。
此外,它还可以防止带符号零的一些问题。例如,如果 a 和 b 为负,则 (0.0+i*a)*(0.0+i*b) 计算结果为 (-a*bi*0.0),否则为 (-a*b+i*0.0)。如果将结果输入到具有分支切割的函数中,这可能会令人惊讶。虚数类型避免了这种不必要的零否定。
我的问题是你能在 Haskell 中定义一个类似的虚数类型(除了复杂类型之外)以及操作(+), (-), (*), 和(/)for 它,这样它们的行为就像在 C++ 中一样?看来至少在Num和Fractional类的当前定义中这是不可能的,因为(+), (-), (*), 和(/)具有a -> a -> a类型签名,因此例如两个虚数相乘不能得到不同的类型。然而,是否可以对这些类有不同的定义,以便我所追求的目标成为可能?
我问这个并不是为了实际目的。我只是想更好地了解 Haskell 类型系统的功能。