为什么区分联合与函数式编程相关联?


30

在多年的OO编程中,我了解了区别工会是什么,但我从未真正错过过他们。我最近一直在用C#进行一些函数式编程,现在我一直希望自己拥有它们。这让我感到困惑,因为从表面上看,区别工会的概念似乎与功能/ OO二分法完全无关。

函数式编程中是否有固有的功能,使有区别的联合比在OO中更有用,或者是通过强迫自己以“更好”的方式分析问题,我只是提高了标准,现在要求更好模型?


表达式问题en.wikipedia.org/wiki/Expression_problem可能是相关的
xji

这并不是对这个问题的正确回答,因此我将以评论的方式答复,但是Ceylon编程语言具有并集类型,并且它们似乎正在渗透到其他OO /混合范式语言中-我想到了TypeScript和Scala。Java语言枚举也可以用作区分联合的一种实现。
罗兰·特普

Answers:


45

区别联合确实与模式匹配结合在一起,您可以根据情况选择不同的行为。但是,这种模式从根本上与纯粹的OO原理相反

在纯OO中,行为差异应由类型(对象)本身定义并封装。因此,等同于模式匹配的方法是在对象本身上调用单个方法,然后该方法将由有问题的子类型重载以定义不同的行为。从外部检查对象的类型(模式匹配所做的工作)被视为反模式。

根本的区别是数据和行为在函数式编程中是分开的,而数据和行为则封装在OO中。

这是历史原因。通过合并越来越多的功能,像C#这样的语言正从经典的OO语言发展为多范式语言。


6
有区别的联合/和类型不同于继承树或实现接口的多个类;这是一种具有多种价值的类型。它们也不会阻止封装。客户不需要知道您有多种价值。考虑一个链表,它可以是一个空节点,也可以是一个值和对另一个节点的引用。如果没有sum类型,则无论如何都会通过为值和引用使用变量来最终区别对待的联合,但是将具有空引用的对象视为空节点并假装value变量不存在。
瓦(Doval)'17

2
通常,您最终会在一个类中包含所有可能类型的值的变量,再加上某种标志或枚举以告诉您实例是哪种类型的值,并绕过与该值不对应的变量类。
2015年

7
@Doval通过将接口/抽象基类整体表示类型并通过为每种类型的情况创建一个子类,可以将代数数据类型“编码”为类。要处理模式匹配,您有一个方法,可以针对每种情况使用一个函数,因此,对于顶级界面上的列表,List<A>该方法是B Match<B>(B nil, Func<A,List<A>,B> cons)。例如,这正是Smalltalk用于布尔值的模式。基本上,这也是Scala处理它的方式。使用多个类是不需要公开的实现细节。
德里克·埃尔金斯

3
@Doval:明确检查空引用也不是真正的OO。
JacquesB

@JacquesB空检查是一个实现细节。对于链表的客户端,通常有一个isEmpty方法来检查对下一个节点的引用是否为空。
瓦(Doval)'17

35

在学习函数式编程之前,我已经在Pascal和Ada进行了编程,所以我不将歧视的联合与函数式编程联系起来。

在某种程度上,有区别的工会是继承的二重性。第一个允许轻松地对一组固定的类型(联合中的类型)添加操作,而继承允许轻松地对固定类型的操作添加类型。(如何轻松地将两者都添加称为表达问题;这对于具有静态类型系统的语言来说尤其困难。)

由于OO强调类型,以及功能编程对功能的双重强调,所以功能编程语言对联合类型具有天然的亲和力,并提供语法结构以简化其使用。


7

OO中经常使用的命令式编程技术通常依赖于两种模式:

  1. 成功或抛出异常,
  2. 返回null以指示“无值”或失败。

功能范例通常避免这两种情况,而是返回表示成功/失败原因或值/无值的复合类型。

受歧视的工会适合这些化合物类型。例如,在第一个实例中,您可能返回true或描述失败的某些数据结构。在第二种情况下,包含值或的联合nonenil等等。第二种情况非常普遍,以至于许多功能语言都内置了表示该值/无联合的“也许”或“选项”类型。

当使用例如C#转换为功能样式时,您会很快发现对这些复合类型的需求。void/thrownull就是不正确的使用这种代码感觉。歧视工会(DU)非常适合该法案。因此,就像我们很多人一样,您发现自己想要它们。

好消息是,那里有很多库可以在C#中模拟DU(例如,看看我自己的Succinc <T>库)。


2

在主流OO语言中,求和类型通常不太有用,因为它们正在解决与OO子类型化类似的问题。看它们的一种方法是它们都处理子类型,但是OO是open即可以将任意子类型添加到父类型,而求和类型是closed即先确定哪些子类型有效。

现在,许多OO语言将子类型与其他概念(例如继承的结构,多态性,引用类型等)组合在一起,以使它们通常更有用。结果是,它们往往需要更多的工作(使用类和构造函数以及诸如此类的东西)来进行设置,因此在通用类型化变得普遍之前,它们往往不会用于Results和Options之类的东西。

我还要说的是,大多数人在开始进行OO编程时就了解了现实世界中的关系,例如Dog isa Animal,这意味着Integer isa Result或Error isa Result似乎有些陌生。虽然想法很相似。

至于为什么功能语言可能更喜欢封闭式而不是开放式,可能的原因之一是它们倾向于使用模式匹配。这对于函数多态性很有用,但对于封闭类型也非常有效,因为编译器可以静态检查匹配项是否覆盖所有子类型。尽管我不相信有任何内在的好处(这可能会误解),但是这可以使语言更加一致。


顺便说一句:Scala已关闭继承。sealed表示“只能在同一编译单元中扩展”,这使您可以在设计时关闭子类集。
约尔格W¯¯米塔格

-3

斯威夫特乐于使用区分性联合,但称其为“枚举”。枚举是Swift中对象的五个基本类别之一,它们是类,结构,枚举,元组和闭包。Swift可选变量是可区分联合的枚举,它们对于任何Swift代码都是绝对必要的。

因此,“区分联合与函数式编程相关联”的前提是错误的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.