OOP和FP错在哪里?

面向对象和面向函数一直在争论,实际上纯粹的OOP和纯粹的FP都是极端的,对于OOP来讲:存在的并一定都是对象,函数就不是对象;对于FP来说:存在的并不总是纯粹的,副作用总是真实存在。

What’s Wrong with OOP and FP提出了以上观点。

首先,OOP错在哪里?

对于OOP来讲:存在的并一定都是对象,函数就不是对象。

OOP是错误的,因为其定义“对象”企图适应一切。当到了极致时便是“一切都是对象”。但这种观念是错误的。

你可能会认为,在Python或Scala 函数也是对象。在Python中,每一个对象的方法的__call__是一个函数。同样,在Scala中,对象的方法命名也适用函数。但经过一番深思熟虑,你会看到,这是混淆了传递和根本基础两个概念,函数才是根本基础,而传递的是包装它们的对象。Python和Scala只是绑架了函数,把它们打入了牢狱“对象” ,然后给他们一个标签是__call__或apply,然后再调用这些对象的方法。

当你包装成函数成为一个对象时,你可以使用像一个对象去使用函数,但是这并不意味着你就可以说“函数也是对象” 。

大多数面向对象的语言也缺乏正确实现first-class的函数。一个极端是Java,不允许函数作为数据传递。您可以随时用对象包装函数,称他们为“方法” ,但正如我所说,这是绑架。first-class的缺乏是主要原因,为什么在Java有这么多的“设计模式” 。一旦你有first-class函数,您将不需要任何设计模式。

其次,FP错在哪里?
FP是错误的,当你走到极端,变成一个纯粹的函数性语言,也是错误的,见Amr Sabry :What is a Purely Functional Language

对于FP来说:存在的并不总是纯粹的,副作用总是真实存在。

纯粹函数语言试图重新实现宇宙,通过将宇宙一切输入输出。但是真实世界和模拟之间有一个相差,副作用是真实世界的,它真实存在物理世界,当我们进行模拟计算是也是必不可缺少的,纯函数模拟它们注定是低效的,复杂的,甚至是丑陋的。 Haskell的同样是不正确的。

纯粹的函数性语言造成了巨大的认知成本。如果你看看他们的深入,monads单子使程序复杂,很难写,monad单子变压器只是丑陋的黑客而已,monads同样于设计模式,用单子代表副作用很类似用访问者模式去写解释器一样。

当你使用Haskell做事时,你有没有注意到其他语言实现起来多么容易吗?

Amr Sabry可能是世界上最纯粹的函数式编程语言知识最渊博的人,他通过一个小故事告诉我们:他们使事情不必要地复杂化。

有人说单子的价值是他们“划去了”的副作用,但是单子不能使你的程序更容易分析或安全,这种划去有什么意义呢?

当然过度使用的副作用会让程序更加难以分析,但你也可以在C语言中写纯函数:
int f(int x) {
int y = 0;
int z = 0;
y = 2 * x;
z = y + 1;
return z / 3;
}
你也可以使用汇编写,纯函数不只是属于纯函数语言,你可以在任何语言中编写纯函数,前提是,你得允许也能使用副作用。

回顾历史,数学的理想主义是纯粹的函数性语言的原动力。数学函数是简单而美丽的,但不幸的是,只有当你的模型是纯粹的,它们才工作得很好。否则,它变成难看。

不要害怕“范畴论”等流行语。我知道有相当数量的范畴论。即使类别理论家自己称之为“抽象的废话” ,因为它基本上是一个怪诞的方式表达你已经知道的东西!如果你读了Gottlob Frege弗雷格文章的函数和概念,你会惊奇地发现,大多数数学家在他写作之前就得到了错误的函数,这大概已经追溯到一百年以前。事实上,数学家用其语言已经做了这么多错误的事情,特别是微积分之类的东西。没有理由今天编程语言的设计者应该盲目地学习数学。


[该贴被banq于2013-11-13 14:14修改过]

其实OOP和FP这段恩怨很容易从逻辑上理解。

假设整个宇宙内有两个世界,计算机世界和真实世界,如同神话小说里天国和人间一样。

OOP和FP是计算机世界里面的两个兄弟,OOP是状态机国王生的儿子,FP是图灵机国王生的儿子,娘家是数学一派的。

OOP和FP下凡来到人间真实世界,OOP宣讲教义,一切都是对象,真实世界里的人们觉得这是真理,都相信了OOP,但是FP不高兴了,对OOP说:大哥,在俺们那旮瘩(计算机世界天国),这句话好像不对吧,至少我不是你吧(典型的罗素悖论)。


[该贴被banq于2013-11-13 14:59修改过]

嘛,这篇文章直击我现在掌握的两种语言。我谈谈我见解,对象和函数,从逻辑角度说是,可以相互转换的:构造函数视为主函数,而方法则是一切第一个参数为构造函数返回值的函数,概括起来就是对象是特殊函数的特殊整合(特殊是因为对象去掉了函数的复合性与逻辑能力,这也是根本矛盾)。

关于函数对于副作用的问题,我认为大多数人没有深入认识。时钟,我曾经提到过,指令式没有时钟就根本不能存在。而指令式是混合了时钟的函数。IO与时钟是关系密切的,Haskell引入副作用的方式就是让某种函数与时钟绑定,然后通过范畴方式进行隔离——因为这两种函数根本不能逻辑组合,用一种约定或一种解释,使其自然存在。因为人类对于状态的敏感性,导致指令式更适合而已,但人们没有意识到这种混合性,便随便否定了更适合人类思维逻辑的函数式,没有意识到副作用究竟是怎样的存在,是无法理解函数式的意义。

其实把时钟拿出来思考,我们可以认为,计算机是每单位时间都在计算一个函数而已。状态对时间的敏感性这么强烈,为啥很多人不能想起时钟?
[该贴被SpeedVan于2013-11-13 18:37修改过]

对于FP来说:存在的并不总是纯粹的,副作用总是真实存在。

这段意思用大白话可以这么理解,真正纯洁的人是不存在的,纯洁有时是傻瓜的代名词,语言中经常有褒义词和贬义词,我们经常要褒义的,认为贬义的是副作用,比如像做个纯洁的人,但是不像做傻瓜,其实不可能。但是很多人相信这是可能的,只能说明他被洗脑成理想主义。

还有鲁迅,留取其精华,剔除其糟粕,其实糟粕是精华的副作用,根本无法分离。鲁迅其实是个很感性的人,否定是其唯一语境,而否定是经常会违反逻辑的,如“没有银弹”是一句否定,那么这句本身是否是银弹呢?

回头再谈副作用,垃圾对于你来说无用,是副作用,对于另外一个人是宝。

所以,副作用在真实世界是存在的,是一个事物的两个方面,比如用类的方法来表达函数功能,这在真实世界中还是可行的,因为行为都有主体,每个主体都要对自己行为负责,此为职责,当然这样按照主体结构归类,肯定有副作用,就看你把它安置在什么地方,也就是确定在什么上下文环境里了。

而在计算机世界里,由于都是基于纯逻辑建立,要么是0,要么是1,很纯粹,截然分明,没有模糊性,没有副作用,函数或数学得到了生存的上下文,但是脱离了计算机世界这个上下文,它就变得不现实了。

总结一句:FP是属于天国的,OOP是属于人间的,天上人间,一对活宝。

以上是个人理解。

[该贴被banq于2013-11-13 19:47修改过]

为什么在Java有这么多的“设计模式” 。一旦你有first-class函数,您将不需要任何设计模式。

对这个不太理解

2013-11-14 10:19 "@lostalien"的内容
对这个不太理解 ...

应该是不需要这么多模式吧,见Scala的设计模式:
http://www.jdon.com/45813

2013-11-13 14:11 "@banq"的内容
数学家用其语言已经做了这么多错误的事情,特别是微积分之类的东西。没有理由今天编程语言的设计者应该盲目地学习数学。 ...


数学和软件是有区分。我想通过基本概念的划清,对人的思维有很多帮助。

“软件”一词中有“件”,一件一件的意思,如硬件的“件”一样,有组件组装的设计意思。面向工程了。

而“程序”一词中“序”,可能指CPU的时序,编制的代码要考虑到CPU执行,面向纯计算机科学了。

所以,“软件” 不等于“ 程序”。做软件的人叫软件工程师,编程序的叫程序员。

需求功或用算法实现,或用用逻辑实现。

程序=数据结构+算法;
软件=领域模型+业务逻辑。
前者偏重数学天国;后者偏向尘土人间。

使用算法实现的是数学建模(数学公式是一种模型);使用逻辑实现的是领域建模。

这两种思维世界的人在现实中不断碰撞和争吵。