在纯函数式编程环境中面向对象编程? [英] Object-oriented programming in a purely functional programming context?

查看:119
本文介绍了在纯函数式编程环境中面向对象编程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在函数式编程(FP)上下文中使用面向对象编程(OOP)有什么好处?

我一直使用 F#有一段时间了,我注意到我的函数越无状态,我需要的就越少他们作为对象的方法。特别是,依靠类型推断在尽可能多的情况下可以使用它们是有好处的。



这并不排除对某些名称空间的需求形式,这与OOP正交。也不鼓励使用数据结构。事实上,FP语言的实际使用在很大程度上依赖于数据结构。如果您看看 F Sharp编程/高级数据结构中实现的F#栈, ,你会发现它不是面向对象的。



在我看来,OOP与拥有对对象状态起作用的方法密切相关主要是为了 mutate 这个对象。在一个纯粹的FP环境中,不需要也不需要。

一个实际的原因可能是能够与OOP代码交互,就像F#与< a href =http://en.wikipedia.org/wiki/.NET_Framework =noreferrer> .NET 。除此之外,是否有任何原因? Haskell世界的经验是什么,在这个世界中,编程更纯粹的FP?

我会很感激任何关于这个问题的论文或反事实现实例子。 p>

解决方案

您看到的断开连接不是FP与OOP的关系。它主要是关于不可变性和数学形式主义与可变性和非正式方法。首先,让我们放弃可变性问题:你可以使FP具有可变性,并且具有不变性的OOP就好了。即使功能更强大,Haskell也可以让你用所有你想要的可变数据来玩,你只需要明确什么是可变的,以及事件发生的顺序;和效率问题放在一边,几乎任何可变对象都可以构造并返回一个新的更新实例,而不是改变它自己的内部状态。

这里更大的问题是数学形式化特别是在语言中很少使用lambda演算的情况下,大量使用代数数据类型。你已经用Haskell和F#标记了这一点,但意识到这只是函数式编程领域的一半;与ML风格的语言相比,Lisp家族有一个非常不同的,更自由的角色。大多数目前广泛使用的面向对象系统在本质上都是非正式的 - 形式化确实存在于面向对象中,但它们并没有明确地以FP形式主义在ML风格语言中的方式被明确地调用过。



如果你消除了形式主义的不匹配,许多明显的冲突就会消失。想要在Lisp之上构建一个灵活的,动态的,特殊的OO系统?继续,它会工作得很好。想要将正式的,不可变的OO系统添加到ML风格的语言中?没问题,只是不要指望它能很好地与.NET或Java搭配。






现在,您可能会想知道,是面向对象的适当形式吗?好吧,这里有一句妙语:在很多方面,它比ML风格的FP更加以功能为中心!我将重新参考我最喜欢的论文,看起来似乎是关键区别:结构化数据,如ML风格语言中的代数数据类型,提供数据的具体表示以及定义数据的能力;对象提供了对行为的黑盒抽象以及轻松替换组件的能力。



这里有一个比FP更深的二元对比。OOP:它与某些编程语言理论家称为表达式问题有密切关系:通过具体的数据,您可以轻松添加与之配合的新操作,但更改数据结构更加困难。使用对象可以轻松地添加新数据(例如,新的子类),但添加新的操作很困难(考虑为具有许多后代的基类添加一个新的抽象方法)。



我之所以说OOP更加以功能为中心,是因为函数本身代表了一种行为抽象的形式。事实上,你可以通过使用记录来持有一堆函数作为对象来模拟OO风格的结构,让记录类型成为一种接口或抽象基类,并且具有创建记录替换的功能类构造函数。所以从这个意义上说,面向对象的语言使用的是高阶函数,这比Haskell所说的要多得多。



对于这种类型的设计在Haskell中使用得非常好,阅读 graphics-drawingcombinators软件包的源代码,特别是它使用包含函数的不透明记录类型的方式,并且仅将它们的行为组合在一起。




编辑:我忘了上面提到的一些最后的东西。



如果OO确实广泛使用高阶函数,它应该很自然地适合于Haskell这样的函数式语言。不幸的是,情况并非如此。诚然,正如我描述的那样(参见LtU链接中提到的论文),对象确实很好。实际上,结果是比大多数OO语言更纯粹的OO风格,因为私有成员由用于构造对象的闭包隐藏的值表示,并且除了一个特定实例本身之外其他任何东西都不可访问。你不会比这更隐私!



在Haskell中不能很好地工作 subtyping 。而且,虽然我认为继承和子类型在OO语言中经常被滥用,但是某些形式的子类型对于能够以灵活的方式组合对象非常有用。 Haskell缺乏子类型的固有概念,而手卷替换往往是非常笨拙的。

另外,大多数带有静态类型系统的OO语言使得子类型的完整哈希以及由于替代性过于宽松而不能为方法签名中的方差提供适当的支持。事实上,我认为唯一完全没有搞砸的OO语言,至少我知道的是Scala(F#似乎让.NET有太多让步,尽管至少我不认为它会产生任何新的错误)。我对许多这样的语言的使用经验有限,所以我可能在这里是错误的。



在Haskell特有的注释中,其类型类通常看起来很诱人OO程序员,我说:不要去那里。试图以这种方式实施OOP只会以泪结束。将类型类看作是重载函数/运算符的替代,而不是OOP。

Are there any advantages to using object-oriented programming (OOP) in a functional programming (FP) context?

I have been using F# for some time now, and I noticed that the more my functions are stateless, the less I need to have them as methods of objects. In particular, there are advantages to relying on type inference to have them usable in as wide a number of situations as possible.

This does not preclude the need for namespaces of some form, which is orthogonal to being OOP. Nor is the use of data structures discouraged. In fact, real use of FP languages depend heavily on data structures. If you look at the F# stack implemented in F Sharp Programming/Advanced Data Structures, you will find that it is not object-oriented.

In my mind, OOP is heavily associated with having methods that act on the state of the object mostly to mutate the object. In a pure FP context that is not needed nor desired.

A practical reason may be to be able to interact with OOP code, in much the same way F# works with .NET. Other than that however, are there any reasons? And what is the experience in the Haskell world, where programming is more pure FP?

I will appreciate any references to papers or counterfactual real world examples on the issue.

解决方案

The disconnect you see is not of FP vs. OOP. It's mostly about immutability and mathematical formalisms vs. mutability and informal approaches.

First, let's dispense with the mutability issue: you can have FP with mutability and OOP with immutability just fine. Even more-functional-than-thou Haskell lets you play with mutable data all you want, you just have to be explicit about what is mutable and the order in which things happen; and efficiency concerns aside, almost any mutable object could construct and return a new, "updated" instance instead of changing its own internal state.

The bigger issue here is mathematical formalisms, in particular heavy use of algebraic data types in a language little removed from lambda calculus. You've tagged this with Haskell and F#, but realize that's only half of the functional programming universe; the Lisp family has a very different, much more freewheeling character compared to ML-style languages. Most OO systems in wide use today are very informal in nature--formalisms do exist for OO but they're not called out explicitly the way FP formalisms are in ML-style languages.

Many of the apparent conflicts simply disappear if you remove the formalism mismatch. Want to build a flexible, dynamic, ad-hoc OO system on top of a Lisp? Go ahead, it'll work just fine. Want to add a formalized, immutable OO system to an ML-style language? No problem, just don't expect it to play nicely with .NET or Java.


Now, you may be wondering, what is an appropriate formalism for OOP? Well, here's the punch line: In many ways, it's more function-centric than ML-style FP! I'll refer back to one of my favorite papers for what seems to be the key distinction: structured data like algebraic data types in ML-style languages provide a concrete representation of the data and the ability to define operations on it; objects provide a black-box abstraction over behavior and the ability to easily replace components.

There's a duality here that goes deeper than just FP vs. OOP: It's closely related to what some programming language theorists call the Expression Problem: With concrete data, you can easily add new operations that work with it, but changing the data's structure is more difficult. With objects you can easily add new data (e.g., new subclasses) but adding new operations is difficult (think adding a new abstract method to a base class with many descendants).

The reason why I say that OOP is more function-centric is that functions themselves represent a form of behavioral abstraction. In fact, you can simulate OO-style structure in something like Haskell by using records holding a bunch of functions as objects, letting the record type be an "interface" or "abstract base class" of sorts, and having functions that create records replace class constructors. So in that sense, OO languages use higher-order functions far, far more often than, say, Haskell would.

For an example of something like this type of design actually put to very nice use in Haskell, read the source for the graphics-drawingcombinators package, in particular the way that it uses an opaque record type containing functions and combines things only in terms of their behavior.


EDIT: A few final things I forgot to mention above.

If OO indeed makes extensive use of higher-order functions, it might at first seem that it should fit very naturally into a functional language such as Haskell. Unfortunately this isn't quite the case. It is true that objects as I described them (cf. the paper mentioned in the LtU link) fit just fine. in fact, the result is a more pure OO style than most OO languages, because "private members" are represented by values hidden by the closure used to construct the "object" and are inaccessible to anything other than the one specific instance itself. You don't get much more private than that!

What doesn't work very well in Haskell is subtyping. And, although I think inheritance and subtyping are all too often misused in OO languages, some form of subtyping is quite useful for being able to combine objects in flexible ways. Haskell lacks an inherent notion of subtyping, and hand-rolled replacements tend to be exceedingly clumsy to work with.

As an aside, most OO languages with static type systems make a complete hash of subtyping as well by being too lax with substitutability and not providing proper support for variance in method signatures. In fact, I think the only full-blown OO language that hasn't screwed it up completely, at least that I know of, is Scala (F# seemed to make too many concessions to .NET, though at least I don't think it makes any new mistakes). I have limited experience with many such languages, though, so I could definitely be wrong here.

On a Haskell-specific note, its "type classes" often look tempting to OO programmers, to which I say: Don't go there. Trying to implement OOP that way will only end in tears. Think of type classes as a replacement for overloaded functions/operators, not OOP.

这篇关于在纯函数式编程环境中面向对象编程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆