函数式编程(Functional programming)

  函数式编程(简称FP)不只代指Haskell Scala等之类的语言,还表示一种编程思维,软件思考方式,也称面向函数编程。 编程的本质是组合,组合的本质是范畴Category,而范畴是函数的组合。

  首先,什么是函数式编程,这并没有唯一定义,它只是广泛聚合了一些编程风格的特性,我们可以将它与面向对象编程OOP进行对比,两者区别是,OOP最大优点是多态性和封装;而FP优势是不变性及其声明性风格,两者其实是十字正交,可互补的,可在同一程序中共存。

面向对象:

  1. 数据和对数据的操作紧紧耦合,封装在一个类Class中(充血模型)。这里的数据是可变的。
  2. 数据的变化更改需要通过数据的操作方法实现,类以外方法不能直接操作更改一个类内部的数据属性。
  3. 类或对象隐藏它们操作的实现细节,其他对象调用这些操作只需要通过接口。
  4. .核心抽象模型是数据和数据的关系。
  5. 增加功能通常是组合新对象和拓展已经存在的对象,并加入新的方法实现的。

函数编程:

  1. 数据与函数是松耦合的,两者是分离的,数据是纯数据(失血模型),函数没有与数据封装在一起。这里的数据是不可变的。
  2. 函数隐藏了它们的内部实现,语言的抽象是函数,以及将函数组合起来表达。
  3. 核心抽象模型是函数和函数的组合交换。
  4. 通常增加功能是通过编写新的函数实现。
  5. 变量缺省是不可变的,一旦赋值就不能再被改变,减少可变性变量的使用会提升并发性。

  函数风格的编程特点:

  1. 第一等公民是函数(不是对象Object或Class)
  2. 带有闭包的Lambdas/Anonymous函数
  3. 不变性,大部分无态处理,没有状态和变量(没有可变数据)
  4. 高并发
  5. 无副作用的调用(一个函数不会实现两个以上功能)
  6. 通过tail call实现递归的性能优化。
  7. 模式匹配(Haskell, Erlang)
  8. 懒赋值(Miranda, Haskell)
  9. Homoiconicity(类似LISP)

  如果说OOP还有很多人可能受静态数据思路影响,那么FP 带来完全是动态事件,FP让我们直接用动词思考,用方法函数解决问题,比如两个帐号之间的转帐,按照DDD等静态领域建模思维,转帐这个功能是放在帐号这个实体类中,还是做一个服务呢?在OOP语言中,我们实现功能总是使用服务Service这样一个对象实现,而且强调无态服务,无态服务实际就是一个只有函数没有数据的空架子“类”而已。 银行账户之间转帐的老式做法是使用数据库事务,这种做法比较刚性,正确做法是将转帐作为事件存储起来,如果你是一个函数式思维者觉得这样做就很正常。

  有很多人将FP归结于数学思维,实际上这只看到其表面,没有看到数学语言这个背后的形式逻辑,编程语言作为和数学同等形式语言,他们的核心基础都是分析哲学的形式逻辑,过去的面向对象很多设计原则也来源于形式逻辑,见:蒯因与引用透明 。  

  面向对象和面向函数一直在争论,实际上纯粹的OOP和纯粹的FP都是极端的,对于OOP来讲:存在的并一定都是对象,函数就不是对象(没有纯粹的对象);对于FP来说:存在的并不总是纯粹的,副作用总是真实存在(没有纯粹的函数)。总之,面向对象侧重于分解,函数编程侧重于组合,两者配合使用才能发挥各自优点。

Java函数式特点:

  1. 不可变性:写尽可能多不可变对象。这样可以减少我可以更改程序状态的地方。这样可以减少错误。
  2. 参照透明性:写尽可能多的纯函数。这些函数就像数学函数一样。对于相同的输入,它们始终具有相同的输出。
  3. 纯函数和不纯函数:将纯函数与不纯函数分开,可以更轻松地测试代码,并提高了代码的可重用性。
  4. 函数组合:提高代码的可读性,并且通过组合函数来编写代码更容易。
  5. Curried Functions:出色的概念,但是在Java中使用它并不自然。
  6. 惰性求值:在需要时求值。Lambda是在Java中进行惰性求值的方法。
  7. 高阶函数:在现有函数中接收其他函数作为参数。这些函数通常是实用程序函数。
  8. 映射,过滤,reduce模式:Java Stream API实现此模式。
  9. Monads:帮助了解如何安全地处理不纯函数。
  10. Optional类:FP提供有关如何使用可为空的对象的有效解决方案。总是在可以返回null的方法中返回Optional对象而不是null。
  11. 铁路Railway编程:可帮助了解Stream API和Optional类的工作方式。

 

文章

函数式编程 vs 面向对象编程 vs 过程式编程

类型系统和逻辑

函数式编程模式:单位元(幺元)

函数式编程模式:半群

范畴category:组合的本质

什么是Monoid ?

什么是Monad?

为什么需要Monad? 

面向对象与函数编程的比较

OOP和FP错在哪里?

为什么组合好于继承? 

什么是流式思维?

Reactive设计语言与范式

蒯因与引用透明

为什么我再也不使用MVC框架了?

微服务的最终一致性与事件流

一张图解说Map/filter/reduce

从CRUD编程切换到事件溯源和区块链编程

一篇有关函数式编程的形象生动教程

否定OOP的面向数据编程DOP原理

 

Java8

Closure Lambda和Monad

分分钟学会Java8的Lambda

Java 8十个lambda表达式案例

Java8教程

使用Java8的Lambda实现的一个简单案例

使用Java8的Lambda实现Monda

使用Java8的Lambda实现模板模式

使用Java8的Lambda实现策略模式

Java8的Lambda和排序

用Java 8 lambda优化JDBC

使用Java8的Lambda简化ReadWriteLock

Java 8的Lambda表达式的阴暗面

用monad替代嵌套回调

更多Java8专题

 

其他语言

HASKELL入门起步

Scala入门之函数编程

Scala入门之基本概念

更多Scala专题

Javascript闭包是什么?

Scala中文教程和手册

苹果Swift语言中文简明教程

Javascript的函数式编程术语解释

Python语言教程手册

 

专辑

函数式编程系列专题

stream流处理模式

程序员组合思维