为什么Haskell很重要


出于我最近对Haskell的兴趣,我想分享一下我对这门编程语言的经验和想法可能是个好主意。所以我决定写一系列的文章来表达我个人对于为什么我认为Haskell可以很好地适合于企业应用程序开发的看法。我将在这些文章中尝试在不同的层次(企业,架构和软件模块)上提出我的想法。本文的目的不是批评任何主流编程语言,而是对Haskell为什么对企业开发有好处做一个有进取心的介绍。

在这篇文章中,我将尝试深入研究编程语言中可用的抽象工具。我将尝试观察这些抽象构造对整体设计和体系结构的影响。

OOP 类和继承是有用但有限的抽象工具

在主流编程语言中(像C#和Java),程序员大多使用类作为主要的(如果不是唯一的)抽象工具。OOP语言中的类受益于继承作为泛化的工具。继承确实是一个有用的概念,可以将概括和抽象组织到一个层次结构中,在这个层次结构中,每个层次都引入和添加更多的功能,构建在更高的(父)层次上。然而,这些语言中的类既可以用来抽象数据结构,也可以用来抽象函数和行为,实际上这就是它们在这里使用的全部思想:使数据结构与相关行为发生某种内聚。数据结构和相关行为(所谓的方法)之间的这种耦合对抽象和泛化,层次结构和概念重用有负面影响。我的意思是,在建立在这些基础上的类层次结构中,不能选择只重用行为代码或只重用数据结构,因为它们是相互绑定的,行为是为其关联的数据结构而不是为其中的属性而构建的。

人们可以争辩说多重继承为这个问题提供了解决方案。使用多重继承,您可以选择组合一个类中的功能,并用另一个类表示您的数据类型,然后您可以选择仅继承数据结构,或仅继承功能,或两者兼而有之。但是在发现它的局限性之前,你不能进一步使用这种方法。这里我们仍然有同样的问题,我们不能将行为建立在数据结构属性的基础上,而不将两个属性合并到一个类中,从而带来相同的耦合并强制扩展器继承这两个属性。

理想情况下,我想要达到的是一个数据抽象和函数抽象不是内在耦合的系统,一个人可以自由地基于彼此构建抽象。通过使数据抽象和函数抽象独立变化,您可以实现更多的代码重用:不必绑定到特定数据结构的抽象可以被识别,尽管可以被满足某些条件的任何数据类型继承。

以可比性属性为例:我们可以比较类的不同对象,如果类实现了IComparable我们可以说是运营部小于(反对对方) 比(反对对方)伟大等于

这里,IComparable可以基于Iequatable它指定处理相等性的操作,假设它们是相等的,并且NoteQuals这样就不需要实现Equals操作来满足IComparable合同。此外,在本例中,我们只需要实现小于对于目标类,因为大于可在以下方面实施Iequatable连同小于实现,并且此实现可以推广到任何想要实现IComparable

在Java或c#中,这可以使用一个实现大于就方法而言小于这是抽象的,而等于从其基类(Iequatable)。这样做的问题是,在这样做之后,您已经利用了唯一的机会扩展了另一个类。换句话说,在不支持多重继承的语言中,如果另一个属性不属于同一层次结构,则不可能以相同的方式(基于抽象类)满足该属性

接口呢?接口并没有为这个问题提供任何直接的解决方案。接口将整个实现留给实现类,因此不泛化任何实现代码(不促进实现泛化)。

支持多继承的语言中的多继承部分地解决了这一限制。但是,这两种方法不仅限制了设计时间的延长,而且也限制了申报地点的延长。这使得扩展来自另一个API或包的类或类型变得非常困难。所以在我们前面的示例中,没有办法创建一个类型可比的而不引入扩展它的另一个类型。

扩展方法是一种方法

扩展方法是C#3.0中的一个新特性。使用extension方法,您可以在类型的声明站点之外扩展该类型。我所说的扩展是指添加对类型进行操作的方法,在扩展方法的情况下,它们可以通过使用点“。”来发现,就像它们是实例方法一样(解释扩展方法不在本文的范围之内)。扩展方法允许在类的声明范围之外扩展类,这解决了我们的一个问题。然而,扩展方法有一个主要的限制:不能基于其他扩展方法的约束来构建某些扩展方法的定义。它们是不可合成的。(C#团队不得不修改编译器以引入LinQ表达式语言,而LinQ表达式语言需要实现一些特定的扩展方法来为您的类型启用它)。

Haskell中的类型类

类型类是我们设计问题的完美解决方案。首先,类型类在数据抽象和函数抽象之间提供了非常干净的分离。类型类看起来有点像java和C#泛型接口,但有些不同:

1:类型类允许默认实现,从而启用实现泛化

2:类型类实例声明(特定类型的接口实现)可以在类型声明站点之外完成,甚至可以在包含该类型的整个模块之外完成。这消除了扩展性的任何限制。

因此,使用类型类,您可以轻松地扩展现有类型以满足您定义的属性,并在此基础上构建以促进广泛的实现通用化和代码重用。