可以将功能语言视为一种类别,其中它的对象是类型,而词素之间是功能。
类型类如何适合此模型?
我假设我们应该只考虑那些满足大多数类型类所具有的约束但在Haskell中未表达的约束的实现。例如,我们应该只考虑Functor
针对fmap id ≡ id
和的那些实现fmap f . fmap g ≡ fmap (f . g)
。
还是类型类型有其他理论基础(例如基于类型化的lambda计算)?
可以将功能语言视为一种类别,其中它的对象是类型,而词素之间是功能。
类型类如何适合此模型?
我假设我们应该只考虑那些满足大多数类型类所具有的约束但在Haskell中未表达的约束的实现。例如,我们应该只考虑Functor
针对fmap id ≡ id
和的那些实现fmap f . fmap g ≡ fmap (f . g)
。
还是类型类型有其他理论基础(例如基于类型化的lambda计算)?
Answers:
类型类如何适合此模型?
简短的答案是:他们没有。
每当您在语言中引入强制性,类型类或其他用于特殊多态性的机制时,面临的主要设计问题就是一致性。
基本上,您需要确保类型类解析是确定性的,以便类型正确的程序具有单一解释。例如,如果您可以在同一范围内为同一类型提供多个实例,则可能会编写如下这样的模棱两可的程序:
class Blah a where
blah : a -> String
instance Blah T where
blah _ = "Hello"
instance Blah T where
blah _ = "Goodbye"
v :: T = ...
main :: IO ()
main = print (blah v) -- does this print "Hello" or "Goodbye"?
根据编译器选择的实例,blah v
可以等于"Hello"
或"Goodbye"
。因此,程序的含义将不会完全由程序的语法确定,而是会受到编译器做出的任意选择的影响。
Haskell解决此问题的方法是要求每种类型每个类型类最多具有一个实例。为确保这一点,它仅允许在顶级执行实例声明,并且还使所有声明在全局范围内可见。这样,如果进行了模棱两可的实例声明,则编译器始终可以发出错误信号。
但是,使声明在全局范围内可见会破坏语义的组合性。要恢复,您需要做的是为编程语言提供详尽的语义 -即,您可以展示如何将Haskell程序转换为行为更佳,组成更多的语言。
实际上,这实际上也为您提供了一种编译类型类的方法-在Haskell圈子中通常称为“证据翻译”或“字典传递转换”,并且是大多数Haskell编译器的早期阶段之一。
类型类也是编程语言设计与纯类型理论不同的一个很好的例子。类型类是一个非常了不起的语言功能,但是从证明理论的角度来看,它们的行为很不正确。(这就是为什么Agda根本没有类型类的原因,也是为什么Coq使其成为启发式推理基础结构的一部分。)