这几天有三个关于语言类讨论的帖子:
看了讨论内容或多或少都有些提到了 FP 的概念, 貌似 FP 概念在大众已经流行起来了?
结合今年我们老板发的一篇为什么函数式编程没有流行?, 以及今年我们公司全员学习和使用 Clojure, 博客最后有一段:
未来是 FP 的,现在已经是大概率事件。
OO -> 混合 -> FP
FP 这个词注定会被用烂,这是另外一个故事了。
想听一下大家对 FP 语言的看法, 未来发展如何, 以及你们平时使用过的 FP 语言有哪些? 对比 OO 有哪些优势劣势? 你们有在业务项目中使用 FP 语言吗?
(这不是一个关于某种语言讨论的帖子, 不想引起战争)
1
cmdOptionKana 2020-01-09 13:39:20 +08:00 6
1. 纯 FP 没有未来。
2. FP 的其中一些优点会被发扬光大。 3. 纯 FP 太考验智商了…… 而智商就是成本,不管谁投资搞项目都希望降低成本,因此如果招聘普通智商的程序员就能完成任务,老板就不可能花更高价钱去招聘特别高智商的程序员。 |
2
ma836323493 2020-01-09 13:42:17 +08:00 via Android
数据和函数分离,容易造成函数重复,
|
3
abcbuzhiming 2020-01-09 13:44:56 +08:00 1
我不认为 FP 真的流行了,别的不说,FP 的核心概念,“变量不可变”的特性有几个人真的用了?所谓 FP 流行不过是函数像参数传来传去这一点被 js 带起来了而已。
未来不是 fp 的,如果未来是 fp,那么 erlang 这种纯函数语言早就该流行了。所以未来肯定还是混合的 |
4
rogwan 2020-01-09 13:47:42 +08:00 via iPhone
项目终归还是要人来维护的,一方面是团队规模,一方面是项目生命周期,这两个方向一扩展,很多人很多年的项目,用 fp 不敢想象
|
5
FaiChou OP @cmdOptionKana #1 3 是支持观点 1 的吗? 可以解释为什么 FP 考验智商吗?
我可以先讲个例子: 在 js 中处理复杂数据一般用 Immutable.js 或者用 map/reduce/filter/concat 等常见的 no-side-effects 函数, 而这刚好是 FP 的本职, 比如 clojure 的所有函数, 完全可以处理任何复杂数据, 不限于 js 这几种高阶函数, 并且如果觉得写的 FP 代码难以理解, 可以搭配使用 https://github.com/redplanetlabs/specter 等工具. 所以我认为 学习 100 种 FP 的方法处理问题, 比使用几种方法组合来处理问题要简单直白的多. |
6
westoy 2020-01-09 13:51:47 +08:00
07~08 年那会儿最火吧
然后就熄火了........ |
7
FaiChou OP |
8
luozic 2020-01-09 13:54:08 +08:00
缺点:
1.纯函数式难以利用之前几十年大部分积累的面向对象的软件工程实践。 2. 纯函数式比较适合数据处理,不适合复杂业务建模。 优点: 1.合理规划之后的纯函数式可以无缝由 iaas----paas---saas- serverless 2.并发比较容易实现。 人员 ide 缺点: 1.fp 的算法和面向对象的根本不一样,有大量的转换培养成本。 2.fp 的基础设施问题,现在支持 fp 的基础设施少得不行,基本都得借助 MQ 来解耦传统设施对于函数式架构的污染。 3.fp 的优化,debug 等等,友好的 ide 比较少。 |
9
akehgnaix 2020-01-09 14:00:17 +08:00
@luozic 说出了我的心声。FP 对数据处理有好处,但是面对业务的过程式逻辑时还需要翻译为函数式表达,不仅仅考验智商,兼职浪费时间。
FP 强调的不可变导致的内存资源浪费也是非常可怕的 |
10
wangyzj 2020-01-09 14:03:18 +08:00
fp 除了看着语法优雅之外还有啥优点?
|
11
FaiChou OP @wangyzj #10 语法优雅不见得. 我觉得优雅的是 Objective-C: [PageAViewController doSomethingBy: modelB], 哈哈哈, 开个玩笑, 但好像真没见过几个说 fp 语法优雅的, 一大堆括号(lisp 系列)有啥优雅的?
@akehgnaix #9 业务的过程式逻辑翻译为函数式表达? 这里我觉得翻译为对象更考验能力. fp 只关心数据结构如何, 以及用什么工具处理数据, 从数据库 /redis/内存中的数据到用户客户端的展示, 不是将数据变成界面吗? 难道有比 fp 更适合处理数据的吗? 最终调用客户端的 api 来展示数据完事, 中间的数据处理交给函数. 另外 fp 内存浪费之说, 更不敢苟同, 比如 fp 可能会经常写递归, clojure 没有尾递归优化, 但提供 recur 和 lazy-seq 来处理. 所以内存浪费可能是程序员但事, 和编程语言无关. |
12
xuanbg 2020-01-09 14:32:48 +08:00
函数式的语法对比非函数式的语法如 Java 有什么优势吗?这个问题的答案是:相对好处而言,劣势更加明显。所以,函数式编程不存在流行的基础。你可以学习,但不必非函数式不可。
|
13
zhuangzhuang1988 2020-01-09 15:22:18 +08:00
// Inspired by http://www.cs.utexas.edu/~wcook/Drafts/2006/MemoMixins.pdf
// State Monad combined with Continuation Monad (StateT Monad transformer) type StateContMonad<'s, 'a, 'r> = StateContMonad of ('s -> ('s -> 'a -> 'r) -> 'r) // Computation Builder type StateContBuilder() = member self.Return value = StateContMonad (fun state k -> k state value) member self.Bind(StateContMonad contStateMonad, f) = StateContMonad (fun state k -> contStateMonad state (fun state' value -> let (StateContMonad contMonad') = f value contMonad' state' k)) member self.Delay( f : unit -> StateContMonad<'s, 'a, 'r> ) = StateContMonad (fun state k -> let (StateContMonad contStateMonad) = f () contStateMonad state k) let memo = new StateContBuilder() // Tell me Y let rec Y f v = f (Y f) v // Map functions let check (value : 'a) : StateContMonad<Map<'a, 'r>, option<'r>, 'r> = StateContMonad (fun map k -> k map (Map.tryFind value map)) let store (argument : 'a, result : 'r) : StateContMonad<Map<'a, 'r>, unit, 'r> = StateContMonad (fun map k -> k (Map.add argument result map) ()) // Memoization Mixin let memoize f argument = memo { let! checkResult = check argument match checkResult with | Some result -> return result | None -> let! result = f argument do! store (argument, result) return result } let execute f n = let (StateContMonad contStateMonad) = Y (memoize << f) n contStateMonad Map.empty (fun _ value -> value) // Example let big (value : int) = new System.Numerics.BigInteger(value) let fib f n = if n = big 0 then memo { return big 0 } elif n = big 1 then memo { return big 1 } else memo { let! nMinus1Fib = f (n - big 1) let! nMinus2Fib = f (n - big 2) return nMinus1Fib + nMinus2Fib } execute fib (big 100000) 弟兄们 来学习吧 |
14
FireFoxAhri 2020-01-09 16:13:37 +08:00 via Android
Scala 路过…
|
15
0x11901 2020-01-09 16:15:43 +08:00
讲道理我 3 年前就觉得该学 FP 了,然后想了想直接梭哈上 Haskell 吧!!!
好的,我现在还没有入门…… |
16
cyspy 2020-01-09 16:20:00 +08:00
面向数据的映射而非面向步骤,这一点对代码可读性提升巨大
|
18
FaiChou OP @zhuangzhuang1988 #13 你这个例子很好证明 FP 「考验智商」这一点, 可能很多新手都会被这些概念吓住吧
|
19
XIVN1987 2020-01-09 16:50:44 +08:00
RISC vs CISC
微内核 vs 宏内核 FP vs OO 最终流行的肯定不是纯 FP 或纯 OO 编程语言,,而是混合编程语言 |
20
dongeast52123 2020-01-09 16:52:51 +08:00
不知道该说什么。
正如我所使用的主要语言—JAVA,从我刚接触的时候(十年前)就有人说 JAVA 已死,结果十年后依旧是企业级应用的老大。而且短时间内其他语言没有任何赶超的期望。 现在的情况是喜欢函数式编程的在说面向对象陈旧,迟早要完蛋。 我总结这些年的各种推测就是。所有的推测都是扯淡,毫无意义,面向对象和 JAVA 至少能活到下一个十年。 |
21
zjsxwc 2020-01-09 16:53:58 +08:00
得屌丝者得天下,曲高和寡很常见
|
22
autoxbc 2020-01-09 16:55:20 +08:00
纯函数式不好,就像纯素食不好一样
|
23
FaiChou OP |
24
autoxbc 2020-01-09 17:27:02 +08:00
|
25
cigarzh 2020-01-09 17:33:23 +08:00
前端娱乐圈
几个技术并没那么好的 KOL 瞎吹一顿吹火了而已 FP 又不是啥新东西,甚至比 OOP 更老一点 没想过为啥现在是 OO 的天下么? 每天都有人喊 Java 死了,Java 不还是老大哥? |
26
mazyi 2020-01-09 18:12:34 +08:00
局部改良
|
27
zhuangzhuang1988 2020-01-09 18:23:46 +08:00
let char c (s : string) = seq { if s.Length > 0 && s.[0] = c then yield s.Substring(1) }
let (=>) l r s = seq { for sl in l s do for sr in r sl -> sr } let (<|>) l r s = seq { yield! l s; yield! r s } let rec (<*>) e s = seq { yield s; yield! (e => (<*>) e) s } let (<+>) e = e => (<*>) e // example c(a|d)+r let pattern = char 'c' => (<+>) (char 'a' <|> char 'd') => char 'r' 再学点鬼画符. |
28
lietoumai 2020-01-09 18:31:13 +08:00
玩 OCamel 的人应该不多吧,但是 JaneStreet 可挣钱了😄
|
29
TypeError 2020-01-09 18:47:38 +08:00 via Android
当然是混用最爽了,Java 8 Stream 不比一堆重复代码方便
|
30
xcstream 2020-01-09 19:01:37 +08:00
要等到 php 灭亡的那一天
|
31
wolfan 2020-01-09 19:14:04 +08:00
量子时代,函数式是不是会变的超牛逼呐?
|
32
araaaa 2020-01-09 19:23:20 +08:00 via iPhone
我现在写 java 同时在用函数式 反应式 面向对象混编🐶
|
33
swsh007 2020-01-09 19:25:01 +08:00 via iPhone
当年是这么吹 c++,opp,cmm5,java
现在大家都活着 |
34
Revenant 2020-01-09 20:03:47 +08:00
erlang 里的变量不可变真的难理解,还有"Let it Crash"的概念,我学了一阵之后弃坑了
|
35
bnm965321 2020-01-10 09:06:26 +08:00
纯函数式不会成为主流,因为路径依赖,而不是它不够好
|
36
lostpupil 2020-01-10 09:13:24 +08:00
@ma836323493 函数式的特点难道不是 data as code 吗。
|
37
lostpupil 2020-01-10 09:14:38 +08:00
Clojure 是个好语言,既有 JVM 平台优势,又有 Lisp 的优雅。
但是其实入门门槛高,并不适合所有人的。 小众语言不流行也是好事啊,物以稀为贵。 |
38
lostpupil 2020-01-10 09:21:32 +08:00 1
@luozic 缺点前两条并不认同,首先
> 1.纯函数式难以利用之前几十年大部分积累的面向对象的软件工程实践。 积累下来的实践并不是意味着最优解,很多也都是糟粕,就好比面向对象一半是显而易见的,一半显而易见是错的。 2. 纯函数式比较适合数据处理,不适合复杂业务建模。 > 模型本身不就是一个形式化的描述,函数式语言不是有更强的 DSL 吗。 |
39
Hoshinokozo 2020-01-10 10:46:44 +08:00
这个时候,我要搬出垠神的博客了 http://www.yinwang.org/blog-cn/2015/04/03/paradigms
|
40
fpure 2020-01-10 11:12:21 +08:00
|
41
fanfou 2020-01-10 12:12:45 +08:00
FP 确实心智负担会有点大
|
42
no1xsyzy 2020-01-10 13:42:46 +08:00 2
纯函数没意义,coq agda 没人用啊
因为纯函数根本不存在 “效果” 面向对象是灾难,直接上翻译版 https://linux.cn/article-11519-1.html 竟然免不得和 #39 和 #40 链的两篇文章一致…… 另外,写 FP ( Racket 弱纯),没动笔时会觉得 “好难啊这怎么做”。 结果开始动笔就很流畅地写完了。 鬼知道我经历了什么。 说 FP 考验智商的,是没写过 FP 的。 |
43
secondwtq 2020-01-10 21:25:02 +08:00 8
居然顶到了某种字数上限,分两段发 ...
首先我无法理解楼主为什么会有在这个社区发这个帖子的想法 ... 虽然是 F2EX,但是我并不期待这里的人对所谓 FP 能有什么高见,至少在这里的人对 FP 有个哪怕是最基础的认识之前 说实话,哪怕是在 HN 上,这种帖子也是引战贴(并且随便一搜就能在同一个站点找出十个战场来),还是去 LtU 抱团取暖吧 ... 关于“FP 概念在大众已经流行起来了”,知乎上有相关问题: https://www.zhihu.com/question/30190384 为什么这两年函数式编程又火起来了? 楼主要想讨论“函数式编程语言的未来”,首先要解决“函数式编程是什么”的问题。这在任何一个主题和函数式编程无直接关系的社区中都是一个无法解决的问题。就连 JavaScript 和 Ruby 这种社区中也免不了三天两头就有人出来科普什么“函数式思维”,说明就算这些一只脚在 FP 里面的语言也没法解决这种问题 ... 我水平有限,不敢为“函数式编程”这个词给出一个足够“函数式”的定义(“函数式编程”这个词背后的 implication 太多了)。但是在我这几年折腾函数式编程的过程中,有那么两点最大的感受: 第一是对于任何 non-trivial 的程序而言,最重要的东西之一是管理复杂度( Complexity )。管理复杂度最重要的手段是模块化,或者按照 FP 的黑话,叫组合( Composition/Composable/Composability )。而这也是 FP 最看重的东西之一。 Composition 是什么意思?就是看上去非常牛逼如同魔法的东西,一点点拆开发现是由非常简单的东西构成的。比如乐高方块组合成完整的乐高玩具,细胞组成器官,器官组成人体,晶体管组合成逻辑门再组合成芯片,if、for 等操作组合成函数,函数再组合成程序,若干个程序模块组合成一个完整的软件系统。比如说人体一定程度上是 composable 的,因为器官可以移植,器官移植需要配型,所以器官又不那么 composable。 在编程语言中的例子比如:C 的宏有不 Composable 的方面(比如使用有副作用的片段展开可能会导致副作用执行多次); goto 不如 if 和 for 更 composable:用 if 和 for 写的程序可以随便拷贝到其他地方,作用还是一样的(在预期之中),但是纯 goto 写的做不到; Checked Exception 的问题也是出在不 Composable 上——使用其他模块除了需要了解模块本身接口之外,还需要折腾模块本身,以及可能的底层模块暴露出的异常;多重继承会出现 Diamond Problem,这意味着在使用多重继承时必须挨个确定每个基类都有哪些基类,也不 Composable。Implicit Coercion 看上去允许不同类型的值之间 compose,但是往往也会伤害 composability:比如 C 中无符号和有符号值之间的比较。锁也是不 Composable 的(见 https://en.wikipedia.org/wiki/Lock_(computer_science)#Lack_of_composability )。JS 中的 callback 相比 Promise 的缺点也是不 Composable:值是编程语言中最 Composable 的东西,并且 Promise 的错误处理也更有利于 compose。 可以发现不 Composable 的地方往往体现为各种各样的“坑”,而 Composable 的地方我们一般都 习以为常。 对于管理复杂度这一问题(比如设计软件),Composition 提供了极其明显的好处:在理想情况下,系统可以被分解为若干模块,模块可以递归地分解为更小的模块。特定的开发者在特定的时刻只需要关心自己正在开发的模块,以及所依赖的模块的接口。模块可以独立地开发与测试,模块之间的依赖被最小化,实现细节和复杂性被隐藏在抽象接口之后。 较差的 Composability 对于简单程序来讲问题不明显,项目越大,造成的麻烦就越大。 函数式编程提供了实现 Composability,管理复杂度的理论。函数式编程提倡通过小函数组合成更大的函数的方式来构建软件,对于 Composability 极为重视(某些 FP 厨经常扯的猫论更是从根上就是关于 Composability 的理论)。 函数式编程同时认为 Side Effect 是 Composability 的最大障碍,虽然“函数式”这个词可以被套在很多东西上,但是 Side Effect 不是其中之一。如果以 Side Effectful 的方式写程序,就意味着当使用某一模块时,必须考虑这个模块是怎么实现的,可能触发哪些 Side Effect,这个模块依赖的模块又可能触发哪些 Side Effect,这些被依赖的模块依赖的模块又可能触发哪些 Side Effect ... 但函数式编程并非完全拒绝 Side Effect (这不可行),而是强调应该 管理 Side Effect。比如 Haskell 强制使用类型的方式标记哪些函数有 Side Effect,哪些函数没有,有 Side Effect 的函数可能会包含哪些 Side Effect。这就强制你把有 Side Effect 和没有 Side Effect 的函数分开,并且对无 Side Effect 的函数实现了 Composable 的目标。 ML、LISP 和 Scala 等则对程序员比较信任,除了变量赋值稍微麻烦一点,使用 Side Effect 和 Imperative 语言没有什么不同。但从语言设计到库再到代码风格都偏向于限制 Side Effect 的范围。 --- 第二是对于简洁、优雅的理论框架的追求。(注意这里的“优雅”不是语法上的“优雅”) 什么叫做“简洁、优雅的理论框架”呢?我举一个和目前 V 红话题相关,F2EX 应该能懂的例子: https://raphlinus.github.io/ui/druid/2019/11/22/reactive-ui.html 这是我前段时间研究 GUI 时找到的一篇文章。文章试图用一个“简洁、优雅的理论框架”,统一目前流行的 React、Flutter 等 GUI 框架的思想——注意意思不是统一这些框架,而是尝试找出这些框架实现思想的共同点,并总结起来。注意文章作者自己就是 Berkeley 的 PhD,同时自己也在开发一个 GUI 框架。 该文章提出: * 所有的 “reactive” 框架(该文章并未明确定义 “reactive” 这个词,只是用它来概括现在流行的 UI 框架)都可以被视为一个 Tree Transformation 的 Pipeline ——从状态的 tree,生成高层的组件 tree,再生成底层的元素 tree,布局、渲染后变成屏幕上的像素。 * Tree 有两种表示形式,一种是 linked data structure (就像数据结构课本上教的二叉树),另一种是 “trace of execution”,即遍历 tree 得到的 flatten 表示。 * 不同的 GUI 框架在实现这个 pipeline 的很多细节有不同。其中比较重要的部分之一是当输入(状态 tree )变化之后,如何高效更新 pipeline 中的其他 tree——也就是 React 中的 Reconciliation,Angular 的 Dirty Checking 之类的。 这个“理论”最牛逼的地方在于它不仅统一了 React 和 Flutter 之类所谓“声明式”( whatever it’s called ...)框架,还通过不同的“Tree 表示形式”,把渲染中间层和 imgui 这种 Immediate 模式也囊括进来,因此说是“Unified Theory”。这个文章是所有关于 GUI 的资料中,对我最有帮助的。 另外作为被王垠在去年 12 月 24 日喷过的人,我发现这套理论与现代优化编译器的设计思想有非常相似的地方。另一个 GUI 框架的开发者在看这篇文章时也有同样的感受: https://blog.anp.lol/rust/moxie-intro,并且他还找出了其他领域的例子。这时候直接搞到我颅内高潮了 ... 其他相关的例子包括: * 天文学在牛顿之前一直在试图解释天体的运行规律。 * 后来的物理学两百年来一直在追求把四种基本作用统一的理论。 * 生物学使用演化论解释不同生物之间不同特征的关系。 * 图形学使用 Path Tracing 渲染所有光学效果。 * 大数据处理基于 MapReduce 模型操作数据(嘛这其实是 FP 的东西 ...)。 * 编译优化领域使用 Polyhedral Model 建模程序的行为。(编译优化好像还不存在所谓“Unified Theory” ... 这是让我感觉很别扭的一点) UNIX 的“一切皆文件”,“一切皆文本”和某些 OOP 语言中的“一切皆对象”其实同样也是非常熟悉的例子。 与之相对的是,很多编程语言的基础是很随意的:C 最开始是为了折腾 UNIX 整出来的,C++ 则是在 C 的基础上的另一个个人项目。JavaScript 是为了跟 Java 的风十天写出来的。PHP 是为了维护个人网站写出来的。Python 的性质也类似。 Java 反倒是最根正苗红的一个——Java 有 Guy Steele ( Scheme 设计者之一)的参与,泛型等则更有 Martin Odersky ( Scala 之父)和 Philip Wadler ( Haskell 设计者之一,同时也在所谓 “Monad” 概念的应用中有重要贡献)的参与。Pascal 同理。 基础“随意”是什么意思?意思就是语言的设计只是为了满足设计者当下的需求,缺少理论的指导。当然一门语言的设计一定有“为了满足某种需求”的目的,但是“我想做个语言来写博客”和“我们实验室想开发个语言来写 Theorem Prover”显然是两种目标。而这些“随意”的语言的设计者,在设计语言的时候,往往对“他眼前所要解决的问题”比对“程序语言设计”这件事要在行的多,换句话说“随意”的语言是由 PL 外行人做出来的 ... C 的成功是 UNIX 的成功,C++ 的成功则是建立在 C 的成功的基础上,PHP 和 JavaScript 的成功是 Web 的成功——它们都不是“语言本身”的成功,而是傍着别人的大腿获得成功的。 理论的指导能带来什么好处? 相对论预言了黑洞,量子力学在半导体上有很多应用,真的有了物理上的统一理论,指不定又会捣鼓出什么东西。 写算法,了解了贪心、动态规划、分治等套路,能帮你更好地理解与设计算法(虽然有很多题还是做不出来 ... 但是如果你不知道这些套路,只会更难)。 在编程语言中,循环可以被看做是递归的一种特例,那么可以把循环拿掉,用递归(以及 PTC )来实现循环。 Exception、Coroutine、Generator、Async/Await 等则可以统一在 Continuation 的概念下,那就只需要实现 Composable 的 First-class Continuation 就行了。 HM 类型系统则可以进行完整的 Type Inference,允许以动态语言的方式写静态语言——整个程序不需要写一个类型,却依然是类型安全的。 函数式语言则会如此设计:首先把 Lambda Calculus 拿来,Lambda Calculus 允许用非常简单的模型,表达足够的抽象。然后使用 Type 来扩展(限制)它,变成 STLC,再加入一些简单的扩展,比如 Parametric Polymorphism,Algebraic Data Type。最后把 int、bool 等 primitive type 放进去。基本上就是一个能用的语言。 整个语言核心就是这个样子,找不到多余的东西。每一个新的 feature 都是对之前语言非常自然的扩展。 再加入 Reference 用来处理 Side Effect,Modules 来组织程序,做个 Type Inference,这就是一个 ML 语言。 最重要的是,所有的东西都是 Composable 的。 当然 ML 自己有不那么 Composable 的东西,比如只支持 Rank-1 Type,可以说是为了 Type Inference 做的妥协。那也可以扩展到 Higher-Rank 和 Impredicative 到完全的 System F (只是这时候理论会告诉你 Type Inference 不好做了)。所有这些并没有限制程序员的能力,而是提供了更好的 Composability。 Side Effect 也不利于 Composability,这方面也有若干方法来管理,同样是为了更好地 compose。 所以说我对一个”函数式语言“的判定最重要的是它有没有建立在函数式理论的基础上。而至于什么乱七八糟的”First-Class Function“”Algebraic Data Type“和”Immutability“之类的具体的特性并不是最关键的,因为它们都是在这个理论上自然的延伸。 另外,我个人并不喜欢 FP 标榜“利于并发”来搞宣传工作,“利于并发”只是 FP 所提供的 Composability 的一个 Side Effect,而 Composability 本身是 FP 理论的一个 Side Effect。冲着“利于并发”折腾 FP 的,大概率会 totally miss the point。 |
44
secondwtq 2020-01-10 21:25:14 +08:00 7
几个 myth:
* 函数式编程需要"高智商"么? 我认为技术社区对函数式编程的陌生感是被建构出来的。或者说进入了”目前 imperative 占了主流,所以大家从一开始就接受的是 imperative 的话语体系,所以会对 functional 有陌生感“的循环。并且这个循环还存在”functional 的资料更少,生态更差“的负反馈。 函数式编程本身做的就是用非常简单的东西 compose 为更加复杂的东西,函数式编程的理论、大多数函数式语言和函数式程序走的都是这个路子,函数式编程本身并不比拼乐高难。 为什么说函数式编程是自然的,符合直觉的?为什么说函数式编程能够 Compose ? 举个例子:为什么要有 closure 和 first-class function 呢? Closure 反映的是 Lexical Scoping,就是我在外面定义的一个东西,在里面一定能用(无论是一个 if 块里面,还是 for 块里面,还是函数里面)。这本来就是一个编程语言应该具有的属性。 first-class function 则是对编程语言中“值”的语义的自然推广——如果一个 int 可以来回传递,可以被存储,如果一个“对象”( OOP 意义上)可以来回传递,可以进行各种操作,那么为什么函数就不可以呢? 所以并不是“我要 FP,所以我要 closure 和 first-class function”,而是“我要一个正常的,不坑的语言,如果这个语言支持 lexical scoping 和 function,那么它就也应该支持 closure 和 first-class function”,closure 和 first-class function 根本就不是“语言特性”,而是正常的直觉和逻辑的结果。 只是因为很多语言有这两个限制,所以如果你用有此限制的语言入门,你就会先入为主的认为编程语言“就该”是这样的。有一天这个语言更新了,加了这两个“特性”,然后网上一堆 blog 就沸腾了——尽管这不过只是你应有的自由而已。 在 VFX 领域,软件市场大多被大厂商的专业软件所垄断,大多数用户也没什么编程能力。然而就是这么一个行业,最近几年刮起了一股”Node“的歪风邪气——不是 Node.js 的那个 Node,而是”节点“意义的 Node。 这是 Maya 中的 Node: 这是 Maya 新加的 Bifrost 特效系统: https://area.autodesk.com/dynamic_resources/area_blog_post_content/3129/html_content/11564262275.jpg 这是 C4D 中的 Node: http://www.llgmotiongraphics.com/wp-content/uploads/2012/09/Pin-Ball-Dynamics.jpg C4D R20: https://www.maxon.net/fileadmin/_processed_/0/f/csm_nodes_preview_c2598d29d5.png Softimage ICE: https://4.bp.blogspot.com/-bkZyxX3FmdY/UXdE4Mlii3I/AAAAAAAAoHY/EHFXSKgxsFk/s1600/Softimage+ICE+and+Maya+Fluids+collaboration.jpg MODO: http://esteldan-3d-graphics.de/contents/Projects_Pics/Car_Rig_3.jpg 这是 Nuke 中的 Node: https://s3.amazonaws.com/pbblogassets/uploads/2013/02/Nuke-Nodes.png 这是 Substance 中的 Node: https://cdna.artstation.com/p/assets/images/images/009/213/162/large/lino-thomas-noodles.jpg 这是 MARI 中的 Node: https://learn.foundry.com/mari/Content/Resources/images/user_guide/node_graph_intro2_680x394.png 这是 Blender 中的 Node: https://pbs.twimg.com/media/ENFE9MyWsAAlAo6?format=jpg&name=large 这是 Clarisse 中的 Node: https://www.isotropix.com/var/img/cms/41/1463651663-lookdev-unified-shading-network-full.jpg 这是 Houdini 中的 Node: https://cpb-eu-w2.wpmucdn.com/coursework.lincoln.ac.uk/dist/7/135/files/2018/10/LTCAO_SS_11.jpg 最有趣的例子是 3ds Max,论技术力,3ds Max 的用户可能比上面的要偏低一点,自动麻将桌在 2011 版本加入了 Node-based Material Editor: https://cdnb.artstation.com/p/assets/images/images/016/919/059/large/paulo-lima-slatematerialeditor.jpg?1553972675,然后又在 2016 加入了 MCG: https://area.autodesk.com/blogs/the-3ds-max-blog/introducing-max-creation-graphs MCG 作者有 FP 背景,直接摊开了说这就是基于 .NET 的“Functional dataflow visual programming language”。 这年头软件不做个 node network,还真不好意思拿出去卖。 (就连 Unity 都在整什么 Shader Graph,Visual Effect Graph ) 你可以去搜,VFX 工资并不比程序员高,一搜都是劝转 CS 的。然而这就是人家干活的东西。还是靠简单的东西 Composite 成复杂的东西,只有“表达式”和“节点”,没有“变量”。 最极端的例子是 Excel,在上面的”Monad 之父插手 Java“之后,还有个八卦是”Haskell 之父水过一篇关于 Excel 和 FP 的关系的 paper“( https://www.microsoft.com/en-us/research/wp-content/uploads/2016/07/excel.pdf?from=https%3A%2F%2Fresearch.microsoft.com%2F%7Esimonpj%2FPapers%2Fexcel%2Fexcel.pdf ),其中写到:Indeed, one of the great merits of spreadsheets is that users need not think of themselves as doing “programming”, let alone functional programming — rather, they simply “write formulae” or “build a model”. MSFT 如果一开始就告诉 Excel 用户他们在”编程“,那估计没人会用 Excel——太多的神秘感都是被建构出来的。不过我倒是相信,如果告诉 Excel 用户他们在做”函数式编程“,可能要比本站的某些用户更好接受——毕竟”编程“这个东西一般大学才学,更别说什么”imperative“了,而”函数“初中就学了。 汉语,英语,德语,阿拉伯语,哪个更难?我作为汉语的 native speaker,在学到大量屈折的语言时,在看到阿拉伯文字时,感受和学习函数式语言是一样的。这也没碍着全世界人讲不同的语言。 (顺便,自然语言作为长期外行设计出来的东西,有很多不 composable 的地方。最简单的就是英语中的大量的不规则动词) 不过我觉得最要命的是,自然语言中每个语言会使用固定的语音集合,不同语言使用的语音集合是不一样的,而在长大之后我就感觉彻底失去了学习新的语音的能力 ... * Java 一直统治世界,函数式编程就没有未来么? 虽然说完全的函数式编程暂且不太讨好,但是这个问题好像忽视了关于函数式编程的特性一直在不断地进入新的语言和旧有语言的新版本这一事实 ... 用 Bartosz Milewski 的话说:Surely you can’t help but notice that there’s been a steady stream of new functional features INVADING imperative languages. Even Java, the bastion of object oriented ...(注意用词 ...) Guy Steele 更是直接说"We were not out to win over the Lisp programmers; we were after the C++ programmers. We managed to drag a lot of them about halfway to Lisp." 最后,就算一个语言里一点”函数式“特性都没有,什么 Global Variables are Evil, High cohesion Low coupling, Don't Repeat Yourself, Separation of Concerns 这些设计思想不也是通用的么?见得越多,你就会越重视 Composability 的作用,为了实现 Composability,你会自然地发现有时候有 Side Effect 不如没有,你会自然地认识到理论框架的重要性,这个过程并不需要给你强制灌输一些什么乱七八糟的“教义”。 --- 我觉得这几个帖子应该引发的思考是:我们真的有过“在意”这件事情么?再看一遍:C 的成功是 UNIX 的成功,C++ 的成功则是建立在 C 的成功的基础上,PHP 和 JavaScript 的成功是 Web 的成功,Java 的成功是 Sun 和 C++ 的成功——它们都不是“语言本身“的成功。 什么是“语言本身”?考虑这样一个问题,当被问到“XX 语言为什么好?”时,你会怎样回答? 它的 IDE 支持好?它的库多?某个平台甚至某个行业钦定了用它?它的工作多(或者好招人)?它的语法“优雅”?或者单纯它的爹比较厉害? 这些都不是“语言本身”的东西。虽然这些东西会影响大家对其的评价(甚至决定你有没有听说过它)。 但是最有趣的事情是,这些东西不仅会影响对语言的评价,还会影响对“语言本身”的评价,能让人把坏的吹成好的,把好的贬成坏的,所谓 dssq。 说来不怕笑话,Haskell 有个(非官方的?) motto 叫“Avoid success at all costs”。这句话 ... 有点歧义,即“((Avoid success) (at all costs))” 和 “((Avoid) ((success) (at all costs))” 两种解释。但是共同的意思是,Haskell 社区早就看出了“Success”和“语言本身”没有什么关系,Haskell 这个“语言本身”不会向其他因素妥协,好的东西就是好的东西,Haskell 不会因为“Success”的需求而拒绝好的东西,也不会因为“Success”的需求而包容坏的东西。 如果有一天 Haskell 真的“Success”了,那么 Haskell 希望它给人的印象是安全、强大、一致、简洁与高效,而不是“xx 在用 Haskell 修福报”“大家都在用 Haskell”。 虽然说我并不认为 Haskell 真的做到了这一点,也并不认为 Haskell 真的能 “Success”。但是对我个人而言,在这一个网络被封锁和监视,数据被泄露与复制,一言一行一举一动都被记录的时代,能遇到这样一个不忘初心,牢记使命,“pure”的东西,是我的幸运(虽然它有点懒)。 类似的问题不只出现在编程语言领域。“Worse is Better”和“The Right Thing”光在计算机领域就对立了好几十年了。 编程语言天天讲什么 first-class,但是实际上“编程语言”这个东西在它的大多数用户心中什么都不是,连 second-class 都不算。 |
45
FaiChou OP @secondwtq 非常感谢, 这个帖子只贴了我们老大的文章结论, 没有把相关论点贴过来, 可能影响讨论. 再者我现在也只是一名语言的学习者, 对语言的发展史了解不是很多, 你所讨论的这些对我有些超前, 一时半会消化不尽, 希望在 2020 年能够丰富我的阅读.
|
46
FaiChou OP @secondwtq 想起来了, 你是这位大佬 https://www.v2ex.com/t/598363?p=1#r_7871265 . 膜拜.
|
47
no1xsyzy 2020-01-11 17:40:14 +08:00
@secondwtq 我崩溃了
花了好长时间去脑内构建的语言,其实就是 Node 方式表示 control flow 和 data flow,类似流程图式的但同样可以做到 reactive…… 还包括多重 continuation (实际上 if 的实现就是模式匹配成功的 continuation 和失败抛出的 continuation )以及主动执行 continuation ( event emitter ) 结果 first-class continuation 一说就全囊括了,一方面我花这么长时间绕了大半圈想出这种设计感觉浪费了(虽然反正是放松大脑用的),一方面不用我自己取名了 |