关于c#:函数式编程中的对象 – 不变性

Objects in functional programming - Immutability

最近,我作为C和C开发人员在Erlang学习编程。我对函数式编程比较陌生。现在,我试图理解像scala这样的语言中的对象是如何工作的。我被教导OOP是关于使用公共方法改变给定对象的状态。这些方法会更改公共属性和私有成员的状态。但现在我听说在函数式编程中,所有对象都应该是不可变的。好吧,我同意一旦被分配的变量(在给定的函数中)应该仍然指向同一个对象。但是,这个"不可变"是否意味着我不能使用公共方法更改给定对象的内部(属性、私有成员)?这使得对象就像简单的数据容器。它提取了它们之外的所有功能。这使得物体的行为更像是C语言中的结构,这对我来说很奇怪。也许我错过了什么?是否可以使用老式的对象,并仍将其视为函数式编程?


你混淆了三个不同的概念。函数编程、可变性和OOP是三种不同的东西。好的。

I have been taught that OOP is all about changing state of given object using its public methods.

Ok.

是和否。在OOP中,重要的是您有对象,它可以同时携带数据和代码(它们的成员方法),并且您可以使用对象的接口与它们交谈,这样对象就可以好的。

  • 调度到被调用方法的具体实现
  • 提供存储在对象所携带数据中的附加信息(在方法实现中访问this)
  • 执行方法实现的代码

没有人规定这个方法调用的作用,或者它必须修改一些状态。好的。

在处理状态时,OOP恰好有助于恢复一些基本的健全性。这是因为应用程序极其复杂的全局状态可以被分割成更小的部分并隐藏在可变对象中。此外,这些可变对象还可以通过禁止直接访问它们的状态,并仅提供一组可修改该状态的受限操作,来尝试维护至少一些局部不变量。好的。

But now I hear that in functional programming all objects should be immutable.

Ok.

他们应该尊重参考的透明度。如果您的对象没有可变状态,并且只有没有任何副作用的方法,那么它就足够进行引用透明了。足够,但不是必需的:对象可以有更复杂的内部结构,但从外部看是完全不变的。缓存就是一个很好的例子。好的。

此外,即使是纯功能程序也不局限于只使用不可变的数据结构。有纯可变状态的东西。其思想是,您的功能程序被用来构建类似于处理可变状态的复杂行动计划的东西。计划本身是一个不变的实体。这个计划可能非常复杂,但由于它是由纯函数构建的,所以仍然可以很容易地进行推理。这个计划只使用纯函数构建,然后可以给一个小而简单的解释器,解释器在可变内存上执行这个计划。这样,您就获得了两个方面的好处:在构建计划时,您具有纯函数的概念简单性,但在对可变数据结构执行此计划时,您还具有接近金属计算的性能。好的。< Buff行情>

但是,这个"不可变"是否意味着我不能使用公共方法更改给定对象的内部(属性、私有成员)?好的。< /块引用>

一般来说,是的。但是,在scala中,不可变性在默认情况下不会被强制执行。您可以决定应用程序的哪些部分足够复杂,可以进行推理,以便将自己限制在纯函数中可能是值得的。其他的一切都可以使用普通的可变结构来实现,如果这更容易的话。好的。

This makes objects just like simple data containers. It extracts all of the functionality outside of them.

Ok.

不,因为对象仍然带着它们的虚拟调度表。然后,您可以从外部请求对象调用方法apply(integer i),对象可能会调用完全不同的东西,例如好的。

1
2
/** get i-th character */
String.apply(integer i)

或好的。

1
2
/** get value for key `i` */
TreeMap.apply(integer i)

如果不将子类多态性重新实现为设计模式,就不能使用C结构来实现这一点。好的。

Is it possible to use objects the old-fashioned-way and still consider it as functional programming?

Ok.

这不是一个全无的游戏。您可以从一种经典的OOP语言(具有可变状态和所有这些)开始,它在某种程度上支持函数式编程范式。然后,您可以查看您的应用程序,并隔离那些需要对副作用进行更精确控制的方面。你必须决定哪些副作用重要,哪些不那么重要。然后,您可以使用更严格的方法来表示真正关键的部分,使用纯函数(从意义上讲是纯的:您需要它们是纯的,也就是说,在没有在它们的签名中明确声明的情况下,不执行任何关键的副作用)。结果将在一个应用程序中混合使用经典的OOP和FP。好的。

使用纯函数编写程序可以被认为是某种原始逻辑中证明的构造。如果你能在需要的时候做到这一点(对纯函数使用更严格的方法),那就太好了;但是如果你能在不需要的时候省略它(对杂乱的副作用函数使用通常的方法),那就太好了。好的。好啊。


OOP is all about changing state of given object using its public methods.

Ok.

这是个谎言!好的。

in functional programming all objects should be immutable

Ok.

这是个谎言!好的。

But does this"immutability" mean that I cannot change the internals (properties, private members) of given objects using their public methods?

Ok.

有严格的不变性和"模拟"不变性。有了"模拟的"不变性,您可以更改内部结构,但不应该产生可见的更改。例如,缓存大量计算仍然可以接受。好的。

Is it possible to use objects the old-fashioned-way and still consider it as functional programming?

Ok.

这取决于对象的变化到底有多精确,以及如何定义fp。实际上,该死的,你可以在fp中改变对象。好的。

Maybe I am missing something?

Ok.

是的,你应该学很多东西。好的。

对于不可变的对象,有几点您不了解:好的。

  • 不可变对象仍然可以具有计算属性。所以它不仅仅是C中的结构,详细信息请参见统一访问原则。注:在C结构中,可以具有计算属性
  • 不变的对象仍然可以有有用的方法。所以它不仅仅是C中的结构,你可以在不可变的对象中有一些逻辑,但是这个逻辑不能改变状态。注:在C结构中,可以具有计算属性。
  • 而且,在内存安全语言(如C,scala,erlang)中,对象和结构之间有很大的区别:您不能轻松地使用结构。一般来说,安全代码中没有指针(不安全区域除外)。所以你不能只在不同的结构之间共享相同的结构。
  • 不可变对象仍然可以封装一些数据。封装是进行抽象(好的或坏的)的关键。
  • 以下是您应该了解的OOP:好的。

  • OOP不需要改变状态。没有突变的OOP仍然是OOP。
  • OOP有一个很有用的东西:继承。纯FP中没有这样的东西。
  • 以下是您应该了解的关于FP的内容:好的。

  • FP不要求您使用不可变的数据。但是在FP中使用不可变数据要容易得多。好的。

  • If a tree falls in the woods, does it make a sound?
    If a pure function mutates some local data in order to produce an immutable return value, is that ok?"

    Ok.

    好的FP语言有很多方法可以处理可变数据以满足性能需求。FP只是喜欢隐藏突变。好的。

  • 只有纯FP语言才迫使您使用不可变的数据。< OK >。

  • FP已经获得了它作为一流公民的职能的名称。在wiki上查看更多信息。您可以像使用&pass对象一样使用&pass函数。好的。

  • 很多FP都不是关于函数的。好的。

  • 您还应该知道OOP和FP是如何关联的:好的。

  • 封装和多态在OOP和FP中的工作方式不同。很清楚封装是如何在OOP中工作的:私有的/受保护的和另一个访问修饰符。但纯FP封装某些数据的唯一方法是使用闭包。
  • 多态性在OOP和FP中的工作方式不同。在OOP中,您使用的是子类型多态性。但是在纯FP中,您应该使用协议(接口)或多方法。
  • "封闭是穷人的对象,反之亦然"。可以用闭包和对象来表示相同的代码。
  • C和scala是多范式语言。它们都支持OOP和FP。把OOP和FP混合起来也不错。实际上,这是产生可读代码的最佳方法。
  • 现在,我想解释为什么有很多人认为有些范例不能结合在一起:好的。

  • OOP诞生于不变性变得有用之前。以前的程序比较简单,我们很担心内存问题。
  • OOP诞生在我们的CPU能够并行代码之前。因此,不变性(这使得编写并行代码更加容易)没有帮助。
  • 在最近普及FP之前,它主要只对学术界人士有用。学术界人士不喜欢不受控制的突变。
  • 潜在的FP程序可以以如此疯狂的方式重新组织,以至于我们不能依赖副作用的时间顺序(以及作为副作用一部分的突变)。没有时间顺序=无用的突变。但是像c,scala这样的语言并没有那么疯狂。但请看下一点。
  • FP几乎是生来就懒得计算的。它们也有单体或类似的机制来取代直接控制流。函数可以随时执行,因为它可以存储在任何地方。这使得副作用(和突变)更加难以控制。
  • 只是为了澄清:FP风格的创造者不是白痴。他们希望副作用得到明确的控制。所以有一个很好的概念:纯函数。好的。

    总结:好的。

    • OOP经常与突变一起使用。但这不是必需的。OOP关注对象。
    • FP常用于不变性。但这不是必需的。FP专注于功能。
    • 你可以混合OOP,FP,突变和不变性——所有这些。

    好啊。


    But does this"immutability" mean that I cannot change the internals
    (properties, private members) of given objects using their public
    methods?

    对.

    This makes objects just like simple data containers. It extracts all
    of the functionality outside of them.

    不,通常不可变类的大部分功能都由它们的构造函数提供。构造函数不必仅仅将传递给它的值作为参数分配给成员变量。功能的另一部分可以在getter类方法中找到,这些方法不仅返回成员变量的值,而且对它们执行更复杂的转换。

    This makes objects act more like structures in C.

    不,C中的结构是完全可变的。任何人都可以访问和修改其元素。