【发布时间】:2017-12-25 19:12:33
【问题描述】:
我目前正试图围绕类型类和实例展开思考,但我还不太了解它们的意义。到目前为止,我对此事有两个问题:
1) 当函数使用某个类型类中的某个函数时,为什么必须在函数签名中包含类型类。示例:
f :: (Eq a) => a -> a -> Bool
f a b = a == b
为什么要在签名中输入(Eq a)。如果== 没有为a 定义,那么为什么不直接在遇到a == b 时抛出错误呢?必须提前声明类型类有什么意义?
2) 类型类和函数重载有什么关系?
这是不可能的:
data A = A
data B = B
f :: A -> A
f a = a
f :: B -> B
f b = b
但可以这样做:
data A = A
data B = B
class F a where
f :: a -> a
instance F A where
f a = a
instance F B where
f b = b
这是怎么回事?为什么我不能有两个具有相同名称但在不同类型上运行的函数...来自 C++ 我觉得这很奇怪。但我可能对这些东西到底是什么有错误的概念。但是一旦我将它们包装在这些类型类实例中,我就可以了。
也可以随意向我投掷类别或输入理论词,因为我在学习这些主题的同时学习 Haskell,我怀疑这些主题中有 Haskell 如何在这里做事的理论基础。
【问题讨论】:
-
“只是抛出错误”在运行时正是静态类型语言试图避免的。拥有类型系统的一个要点是在编译时及早检测某些类型的错误。
-
不是,我的意思是在编译的时候遇到未定义的函数就抛出错误。
-
如果我写了
f :: a -> a -> Bool,然后将其应用于无法比较相等的东西,比如f (1+) (1-),编译器应该如何检测到我们现在需要一个@987654330 @为了抛出错误而定义?答:编译器记录了一个关于f的小事实,它将(==)应用于其参数,以便它知道何时看到对f的调用以检查(==)是否可用。我们如何记下这个事实?答:我们把它放在类型中,为f :: Eq a => a -> a -> Bool。 -
@lotolmencre 啊,也许我开始明白你的意思了。由于您习惯于 C++ 模板,因此您可能会感到困惑。 C++ 模板或多或少地为模板参数的每个实例化编译(包括类型检查)。 Haskell 的(有界)多态函数只编译一次,并且没有“模板实例化”(类似于 Java 的泛型)。在 C++ 中,您需要头文件来包含模板源。在 Haskell 中,编译后不需要源码。
-
如果我必须进行强制比较,类型类与 C++20 概念的关系模糊,除了在 Haskell 中没有 SFINAE:函数是提前编译的(与模板不同),并且约束必须保证所有类型的函数定义都可以(提供约束)。
标签: haskell typeclass overloading