如果我使用+
运算符,Haskell会自动推断类型类Num
:
> let add x y = x + y
> :t add
add :: Num a => a -> a -> a
这是否意味着我不能拥有+
运算符的多个类型类?
答案 0 :(得分:9)
在同一个模块中不能有多个定义+
的类型类(当然,这同样适用于任何其他函数名称 - 而不仅仅是+
)。
如果你导入多个定义+
的模块(无论它是否是类型类的一部分),你需要隐藏+
导入除了其中一个以外的所有模块,导入所有模板其中一人有资格或总是参考+
合格。
答案 1 :(得分:3)
请注意,+
不是运算符,因为它是函数,默认为中缀定位。这意味着它遵循与所有其他函数相同的作用域规则,以及与类型类相同的行为。
具体来说,类型类定义了许多在实例化特定类型类的所有类型中都是多态的函数。因此,Num
的定义为:
class (Eq a, Show a) => Num a where
(+) :: a -> a -> a
(*) :: a -> a -> a
(-) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
我们可以得出结论,任何在其范围内包含Num
定义的内容 - 例如任何导入Prelude
而没有限定条件或排除Num
的内容 - 都将在范围内定义函数(+)
;这是一个带有类型签名a -> a -> a
的普通函数,并且假定为句法糖倾向,因此您可以编写x + y
而不是+ x y
。
特别是,所有这些意味着正如你不能让fmap
成为由类型类Functor
和其他类型类定义的函数一样,你也不能拥有+
1}}由Num
和其他类型类别定义。
如果你想在其他地方定义它,你总是可以导入相应的模块限定 - 但是,当你导入它们时,默认为中缀的非字母函数名称也会非常笨拙。您最终会编写3 M.+ 4
而非3+4
之类的内容。
答案 2 :(得分:0)
我认为问题在于是否有可能拥有多级运营商。这听起来像是对类型类如何工作的误解。如果要实现像向量空间之类的东西,你可能想要搜索类型族扩展,其中参数有不同的类型:
(.*) :: VectorSpace v => Scalar v -> v -> v
对于使用此方法的库,请参阅vector-space包。
答案 3 :(得分:0)
您可以使用+
运算符创建多个类型类,只需通过Prelude隐式导入Num
类实现。添加语言扩展名
{-# LANGUAGE NoImplicitPrelude #-}
或添加一行
import Prelude hiding ((+))
如果你想要一个包含两个类型类的+
,比如Num
和A
,那么你可以创建一个新的类型类来封装两者,
import Prelude hiding (id, (.), (+))
import qualified Prelude
import qualified A
class GeneralPlus a where (+) :: a -> a -> a
instance Num a => GeneralPlus a where (+) = (Prelude.+)
instance A a => GeneralPlus a where (+) = (A.+)