inheritance - 使用函数式编程风格数据类型的模型继承

标签 inheritance f# functional-programming types

我最近一直在使用 F# 并尝试以功能方式进行编码,而不是使用不同的语法重新执行 OOP。我现在遇到了一个问题,我可以通过混合继承和有区别的联合来解决这个问题,但我正试图找到一种纯函数式的表示形式。

我想要建模的是这样的(更改为保留模式,因为我无法使用实际代码):

type Shape =
    | Rectangle of Size * Size
    | Circle of Diameter

到目前为止一切顺利,但现在我需要表示一组与不同类型形状相关的附加属性,例如:
type ShapeProperty =
    | Color of Shape * Color // Fine, valid for all shapes
    | Rotation of Shape * Angle // Wants to be Rotation of Rectangle * Angle
    | Details of Shape * int // Wants to be Detail of Circle * int

如果不是对 Shape 使用可区分联合,而是使用基类和继承,我可以引用实际类型并确保 Rotation 只能应用于 Rectangle 而不是 Circle,但现在我不能。有没有办法在保持纯函数式数据结构的同时实现类似的东西?

编辑:

我目前的解决方案是将单个形状的定义与形状完全相关的事实分开,如下所示:
type Rectangle = Rectangle of Size * Size // Or using a record type
type Circle = Circle of Diameter // Or using a record type
type Shape = RectangleShape of Rectangle | CircleShape of Circle

这意味着我可以在 ShapeProperty 中引用类型:
type ShapeProperty =
    | Color of Shape * Color
    | Rotation of Rectangle * Angle
    | Details of Circle * int

这感觉有点笨拙,因为现在需要将每个形状封装在 Shape 类型中以将它们存储在一个集合中,但它确实给了我一种表达我所追求的类型安全的方法。欢迎对此进行任何改进。

最佳答案

我认为受歧视的工会可能只是这项工作的某些部分的错误工具。仅仅因为你正在编写函数式代码并不意味着你应该抛弃你所知道的一切。

有区别的联合是(在某些方面,有一点需要注意)继承(派生类型)的逆,我们可以称它们为组合类型。

在派生类型模型中,关于父项的所有内容对于子项(LSP)都保证是真实的,在这个组合类型模型中,关于子项的所有内容对于父项都是真实的。(即,而不是“新狗:动物,新猫” :动物,它是新的狗,猫:猫狗,所以你得到了所有关于猫和狗的陈述的新结构。把它看作是关于猫和狗的所有陈述的交集。)

那么问题就变成了,“我们如何在 OCaml 中使用派生类型?”嗯,那里的“O”确实代表对象......

我建议您为形状层次结构创建一个具有派生类型的对象模型,因为您有一个顶部类型(形状)和一个底部类型(空形状,或空)。然后你用这些对象组合你的 ADT,所以你有

抽象的形状,
圆:形状,
矩形:形状,

那么你有

类型 ShapeProperty =
|形状的Foo
|矩形条
|圆的废话;;

任何了解 shape 属性的东西都必须能够处理所有这三种情况(合理,因为这就是您拥有的用途),并且圆形和矩形的赋值与形状兼容,因此您不必担心装箱约定.

对于它的值(value),YMMV 等。 Haskell(运行的其他主要 ML 方言)不使用派生类型的对象,它使用所谓的“类型类”,它们是您“派生自”的属性/行为的集合和“保证实现”从而允许函数接受类型类的具体实例。

虽然它的哲学功能更多,但试图弄清楚发生了什么让我头疼,因为我用于对象之类的构造的传统抽象不再适用,我必须使用这些在逻辑上等效的构造,但在语义上更不寻常(虽然我知道很多人利用它们取得了巨大的成功,所以这可能只是我自己的失败)。

请注意警告,由于标记的联合是标记的,您所要做的就是保证所有标记的语义操作。那个 Action 可能是“失败,'你为什么给我其中一个,我不知道如何处理它'”。您注意到这种结构的问题在于,这样做会丢失很多类型安全非常酷的方面。例如,你写了一个函数,它接受一个选项 int,然后如果选项不是什么你就抛出一个异常,那么,你为什么说你知道如何处理一个选项 int?

关于inheritance - 使用函数式编程风格数据类型的模型继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3643046/

相关文章:

list - Erlang:是否可以将最小函数编写为列表折叠?

scala - 创建二叉树scala的总和树

functional-programming - Elm 中的值类型

Java 方法从继承返回类型

模拟一本书的 Java 类

f# - List.unfold/Array.unfold 导致内存爆裂

generics - 相互定义的参数中的 F# 通用单位

c# - 方法覆盖和可选参数

c++ - 对象数组和继承

f# - 静态解析类型参数需要额外的表达式