为什么学习函数式编程

在阅读DDD巨著《Patterns, Principles, and Practices of Domain-Driven Design》的过程中,Scott在第5章提到了使用函数式编程语言配合贫血模型去实践DDD的一种思路,这激发了我的无限遐想。

在软件开发领域,我们已经拥有了许多的抽象方法论和大量的实现技术。但我个人认为,这一切归根结底,都是人类思维在软件开发领域的具体表达方式。而人类在认识和分析软件所要解决的业务领域问题时,思考的内容不外乎由两个部分组成:『业务流程』『业务规则』。前者,回答了业务活动中先做什么后做什么的问题;后者,则回答了遇到什么情况时应该怎么做的问题。两者结合后,得到我们需要的业务结果,或者叫作“实现业务目标”。

再想想目前学习和掌握的面向对象的一系列方法,又是如何将上述思维结果映射到软件中去的呢?我认为是这样的:

  • 对于业务流程,我们将其表达为若干对象之间的合作,比如UML里序列图的对象与消息,进而具化为具体的类及其职责,比如类及其若干业务方法。
  • 对于业务规则,我们将其表达为若干的判断逻辑,比如UML流程图里的判断分支,进而具化为业务方法里的if-else语句,或者再复杂一点,表达为工厂、策略等设计模式的实际运用。

然后,我认为,对于复杂业务规则的梳理,可以象数学归纳法一样进行演绎:假设一个函数y=f(x),给定x的定义域,确定y的值域。特别是在排列组合等方面的一些问题,也经常采用递归的方式来解决。所以,从这个角度讲,函数式编程更贴近人类思维习惯,所以让我自然而然地把目光转向了它。

为什么选择Scala

在选择具体的函数式编程语言时,我首先想到的是它最好是同时能支持面向对象编程的。因为即便LISP作为函数式编程语言的先祖,诞生已长达半个世纪,但单纯的函数式编程语言与面向对象编程语言相比,在抽象领域概念、组合系统模块、实现信息隐蔽等方面存在一定的差距,所以一直没有成为软件开发的主流。

信息隐蔽原理:在西安电子科大蔡希尧与陈平老师于1993年合作出版的《面向对象技术》一书中是这样描述的:把需求和求解的方法分离;把相关信息——数据结构和算法,集中在一个模块之中,和其他模块隔离,它们不能随便访问这个模块内部的信息。

其次,由于我的语言路线是从Pascal → C → C++ → C#,所以我希望能选择一种风格近似于C、强类型的函数式编程语言。在比较了F#、R、ErLang等几种常见的函数式编程语言之后,我最终选择了Scala。

Scala有何优势

注:以下内容,节选翻译或参考自《Programming in Scala》第1章、第3章,《Programming Scala》第6章,不算完整意义上的个人心得。

函数式编程的优势

  • 纯的函数是没有副作用的。无论何时何地,对于一个函数y=f(x),给定x必定得到y,不会因此产生二义结果。因此无论对于代码测试还是并发,由于给定输入必定得到预期输出,而不受其他因素干扰,所以能有效减少Bug产生。
  • 在函数式编程里,大量使用immutable的值。这意味着函数运算的结果总会创建一个新的实例,避免了通常并发环境下为防止数据共享冲突而采取的保护机制。尽管这需要额外的Copy操作,但Scala针对性地提供了高效的Copy实现,以及延迟计算等弥补机制。
  • 函数是一等公民。函数作为表达式的一部分,可以借由函数之间的嵌套、组合,实现复杂的判断逻辑。

Scala语言本身的优势

  • Scala是面向对象与函数式编程的混合语言,所以能有效结合二者的优点。
  • Scala属于Java生态圈,可以在JVM平台上与Java一起编译运行,所以许多Java的框架、工具都可以直接应用于Scala语言编写的项目。
  • Scala视一切数据类型皆为对象,且支持闭包、lambda、by-name参数等特性,语法简洁明快。
  • Scala使用Actor作为并发模型,与Akka框架自然契合。这是一种区别于传统的、基于数据共享、以锁为主要机制的并发模型,其特点在于以Actor为基本单位、没有数据共享、基于消息传递实现Actor之间的协作,因此可以有效避免死锁、减少竞争。
  • 最后,如果有朝一日要转向大数据领域,有Spark这样的大型框架作为支撑。知乎:与 Hadoop 对比,如何看待 Spark 技术?

Scala对实践DDD有何意义

说了那么多,我的根本目的还是要将Scala作为实现DDD的主要武器。那么试想一下,Scala在我们实现DDD的过程中能有哪些帮助呢?我暂且胡侃乱诌如下:

  • 表示值对象、领域事件等元素更直观。值对象、领域事件在DDD里都应该是immutable的,以往多采取POCO形式表示,现在改用Scala里的val以及case class表示,在语法层面就直观地表明是不可修改的。
  • 在类的方法层面实现CQRS时有语法支持。用Scala里的Function(返回类型为非Unit)对应CQRS里的Query,保证类的方法没有副作用;用Procedure(返回类型为Unit)对应CQRS里的Command,明确表明这一类方法会产生修改状态等副作用。这同样从语法层面就能对二者进行明确区分。
  • 模式匹配丰富了函数操作。除了正则表达式,Scala形式多样的模式匹配语法,为提取数据、完成数据分组聚合等运算、实现逻辑判断提供了强大支持。比如定义def sum_count(ints:Seq[Int) = (ints.sum, ints.size)这样一个函数后,我们可以这样调用,以得到一个1至6的整数序列的整数值合计,及该序列的尺寸: val(sum, count) = sum_count(List(1, 2, 3, 4, 5, 6))
  • 为实现DSL提供有力支持。Scala自带有解析框架,加上灵活的函数语法支持,要自己实现一套DSL及其相应的语法解析器将不再困难。比如在配置文件里这样的一条配置语句,表示退休条件为年龄达到60周岁或者工龄届满30年:retire = (Age >= 60) || (ServiceLength >= 30)。以往的方式是自己写一个语法解析器,把这条文本转换成相应的Specification对象,然后扔给聚合去使用。现在有了Scala的帮助,就使编写语法解析器这一环节的工作量大大减少。
  • 合理的高阶函数设计,使规则编写得到简化。比如打折规则、费用报销规则,以往可能需要若干层的if-else嵌套,现在则将通过高阶函数得到大幅简化。对此,我强烈推荐刘光聪先生的视频Refactoring to Functions,你会在刘先生的重构过程中发现高阶函数的强大。
  • Actor为高效并发打下基础。Actor内部完全自治,自带用于存储消息的mailbox,与其他Actor只能通过消息进行交互,每个Actor都是并发的一个基本单位。这些特点,非常适合于采取Event Sourcing方式实现的DDD。每个聚合都好比一个Actor,在聚合内部始终保持数据的强一致性,而在聚合之间交互的领域事件则好比Actor之间的消息,聚合之间借由领域事件和Saga保证数据的最终一致性。
  • Trait成为AOP利器。Trait是Scala的另一大特色,它就象AOP织入一样,能动态地给某个类型注入方法或者结构。比如配合类Circuit和with后面那4个Trait的定义,val circuit = new Circuit with Adders with Multiplexers with Flipflops with MultiCoreProcessors这样就创建了一个带有加法器、乘法器、触发器和多核处理器的元件。
  • 隐式实现为类型扩展提供支持。对应C#里的静态扩展方法,Scala通过implicit为实现数据类型的方法扩展提供了便捷,成为Trait之外的另一个功能扩展手段。
  • 能降低常见BDD框架的学习成本。尽管这一点可能比较牵强,但我正在努力摸索如何将BDD与DDD结合,而常见的Cucumber、Spock等一些BDD框架,其语法与Scala比较相近,所以我才有如此一说。

有哪些Scala学习资料

以下是我目前主要的学习资料,并衷心欢迎各位留言补充。

书籍

  • Programming in Scala:由Scala语言的设计师Martin Odersky编写,循序渐进,配合了大量实例,入门必读吧。
  • Programming Scala:视角与上面那本有点不一样,没有Scala语言基础会感觉很困难,适合掌握了基本语法后温故而知新。

在线文档与视频

社区

  • Scala 中文社区:不算活跃,原因你懂的。
  • Scala User:Scala入门者聚集地,没有Stack Overflow那么严格,但也需要点爬墙的身手。

SDK及IDE

写在最后

最近读的书很多也很杂,DDD、BDD、Scala、Cucumber以及Java基础等等都有涉及,真恨不得一口吃成个大胖子。由于时间和精力有限,所以现在知识消化很成问题,迟迟没有进入学以致用的环节,只能先这样纸上谈兵了,好歹先把自己在学习过程中的一些思考、看到的好东西先记载下来,以备将来之需。

我为什么想并且要学习Scala的更多相关文章

  1. Scala进阶之路-为什么要学习Scala以及开发环境搭建

    Scala进阶之路-为什么要学习Scala以及开发环境搭建 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 最近人工智能和大数据那是相当的火呀,人工智能带动了Python的流行,区块 ...

  2. Scala 的确棒

    我的确认为计算机学院应该开一门 Scala 的语言课程. 在这篇文章中,我会讲述为什么我会有这样的想法,在此之前,有几点我想要先声明一下: 本文无意对编程语言进行评比,我要讲述的主体是为什么你应该学习 ...

  3. geotrellis使用(六)Scala并发(并行)编程

    本文主要讲解Scala的并发(并行)编程,那么为什么题目概称geotrellis使用(六)呢,主要因为本系列讲解如何使用Geotrellis,具体前几篇博文已经介绍过了.我觉得干任何一件事情基础很重要 ...

  4. Scala初体验

    因为工作中要用到Scala了,本来前面自己还在学习Storm的,没有办法,先把Scala和Spark的这些内容学完在回去看Storm吧! 既然我们要学习Scala,那么我们不禁的要问了,什么是Scal ...

  5. 【Scala】Scala之Classes and Properties

    一.前言 前面学习了控制结构,下面学习Scala的Class和Properties. 二.Class&Properties 尽管Scala和Java很类似,但是对类的定义.类构造函数.字段可见 ...

  6. 【Scala】Scala之Traits

    一.前言 前面学习了Scala中包和导入的相关知识点,接着学习Traits(特质) 二.Traits Scala的特质与Java的接口基本相同,当遇到可以使用Java接口的情形,就可以考虑使用特质,S ...

  7. scala(一)Nothing、Null、Unit、None 、null 、Nil理解

    相对于java的类型系统,scala无疑要复杂的多!也正是这复杂多变的类型系统才让OOP和FP完美的融合在了一起! Nothing: 如果直接在scala-library中搜索Nothing的话是找不 ...

  8. Scala并发编程react、loop代码实战具体解释

    演示样例代码及凝视: //scala并发编程中的react和loop,共同特点: //通过线程存用的方式让性能有所提升. //Actor本身的运行,被actor子系统管理的时候,会有一个或者多个远程的 ...

  9. 如何快速学习Scala

    大数据学习过程中,会学习非常多的技术,但SCALA无疑是必不可少,那我们在大数据技术的学习过程中,如何快速的认识scala,并且学习它,感谢科多大数据公司的余老师提供的详细素材,本人整理成章,希望对你 ...

随机推荐

  1. mysql系列之多实例介绍

    介绍: mysql多实例,简单理解就是在一台服务器上,mysql服务开启多个不同的端口(如3306.3307),运行多个服务进程.这些 mysql 服务进程通过不同的 socket来监听不同的数据端口 ...

  2. 盘点SQL on Hadoop中用到的主要技术

    转载自:http://sunyi514.github.io/2014/11/15/%E7%9B%98%E7%82%B9sql-on-hadoop%E4%B8%AD%E7%94%A8%E5%88%B0% ...

  3. django在windows设置定时任务,勉强能用

    推荐三篇文章 [Django]Django 定时任务实现(django-crontab+command) django中使用定时任务执行某些操作时的规范操作 windows配置crontab 前两篇文 ...

  4. mysql_fetch_assoc查询多行数据

    每次从查询结果中返回一行数据,作为关联数组,类似于一个游标,第一次是返回第一行,第二次迭代就是第二行,以此类推 如果返回多行,使用如下方法就可以了 while($row = $db->fetch ...

  5. sa learning

    后缀数组之前一直在给队友搞,但是这个类太大了,预感到青岛八成会有,于是自己也学习一下,记录一下做题的历程 所用的模板暂时来自于队友的倍增nlogn da算法 int t1[maxn] , t2[max ...

  6. Jenkins Pipeline shell脚本用svn_revision当做系统版本号

    1. 使用dir命令,进入发布目录,版本号所在文件夹. 2. 使用sed命令 修改替换版本号,这里使用vvvv作为要替换的版本号. 3. 最后一步可以不加.只是方便查看效果. stage(" ...

  7. python 使用set对list去重,并保持list原来顺序

    list_one=re.findall(r"^\s{0}[A-Za-z]*\b", txt,re.M) #匹配一级目录 addr_to = list(set(list_one))a ...

  8. shell统计各省的百强县

    原始数据在最后 baiqiang.txt文件中 shell命令: cat baiqiang.txt | grep -P "^国|^☆" | awk -F" " ...

  9. Maximal Rectangle, 求矩阵中最大矩形,参考上一题

    问题描述: Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1 ...

  10. [java]Stream API——collect、reduce、orElse(x)

    一.collect 1.R collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) supplier:一个能创造 ...