王垠:编程的宗派( 四 )
副作用也是某些重要的数据结构的重要组成元素 。 其中一个例子是哈希表 。 纯函数语言的拥护者喜欢盲目的排斥哈希表的价值 , 说自己可以用纯的树结构来达到一样的效果 。 然而事实却是 , 这些纯的数据结构是不可能达到有副作用的数据结构的性能的 。 所谓纯函数数据结构 , 因为在每一次“修改”时都需要保留旧的结构 , 所以往往需要大量的拷贝数据 , 然后依赖垃圾回收(GC)去消灭这些旧的数据 。 要知道 , 内存的分配和释放都是需要时间和能量的 。 盲目的依赖GC , 导致了纯函数数据结构内存分配和释放过于频繁 , 无法达到有副作用数据结构的性能 。 要知道 , 副作用是电子线路和物理支持的高级功能 。 盲目的相信和使用纯函数写法 , 其实是在浪费已有的物理支持的操作 。
fold以及其他大量使用fold和currying的代码 , 写起来貌似很酷 , 读起来却不必要的痛苦 。 很多人根本不明白fold的本质 , 却老喜欢用它 , 因为他们觉得那是函数式编程的“精华” , 可以显示自己的聪明 。 然而他们没有看到的是 , 其实fold包含的 , 只不过是在列表(list)上做递归的“通用模板” , 这个模板需要你填进去三个参数 , 就可以生成一个新的递归函数调用 。 所以每一个fold的调用 , 本质上都包含了一个在列表上的递归函数定义 。 fold的问题在于 , 它定义了一个递归函数 , 却没有给它一个一目了然的名字 。 使用fold的结果是 , 每次看到一个fold调用 , 你都需要重新读懂它的定义 , 琢磨它到底是干什么的 。 而且fold调用只显示了递归模板需要的部分 , 而把递归的主体隐藏在了fold本身的“框架”里 。 比起直接写出整个递归定义 , 这种遮遮掩掩的做法 , 其实是更难理解的 。 比如 , 当你看到这句Haskell代码:
foldr (+) 0 [1,2,3]
你知道它是做什么的吗?也许你一秒钟之后就凭经验琢磨出 , 它是在对[1,2,3]里的数字进行求和 , 本质上相当于sum [1,2,3] 。 虽然只花了一秒钟 , 可你仍然需要琢磨 。 如果fold里面带有更复杂的函数 , 而不是+ , 那么你可能一分钟都琢磨不透 。 写起来倒没有费很大力气 , 可为什么我每次读这段代码 , 都需要看到+和0这两个跟自己的意图毫无关系的东西?万一有人不小心写错了 , 那里其实不是+和0怎么办?为什么我需要搞清楚+, 0, [1,2,3]的相对位置以及它们的含义?这样的写法其实还不如老老实实写一个递归函数 , 给它一个有意义名字(比如sum) , 这样以后看到这个名字被调用 , 比如sum [1,2,3] , 你想都不用想就知道它要干什么 。 定义sum这样的名字虽然稍微增加了写代码时的工作 , 却给读代码的时候带来了方便 。 为了写的时候简洁或者很酷而用fold , 其实增加了读代码时的脑力开销 。 要知道代码被读的次数 , 要比被写的次数多很多 , 所以使用fold往往是得不偿失的 。 然而 , 被函数式编程洗脑的人 , 却看不到这一点 。 他们太在乎显示给别人看 , 我也会用fold!
与fold类似的白象 , 还有currying , Hindley-Milner类型推导等特性 。 看似很酷 , 但等你仔细推敲才发现 , 它们带来的麻烦 , 比它们解决的问题其实还要多 。 有些特性声称解决的问题 , 其实根本就不存在 。 现在我把一些函数式语言的特性 , 以及它们包含的陷阱简要列举一下:
- fold 。 fold等“递归模板” , 相当于把递归函数定义插入到调用的地方 , 而不给它们名字 。 这样导致每次读代码都需要理解几乎整个递归函数的定义 。
- currying 。 貌似很酷 , 可是被部分调用的参数只能从左到右 , 依次进行 。 如何安排参数的顺序成了问题 。 大部分时候还不如直接制造一个新的lambda , 在内部调用旧的函数 , 这样可以任意的安排参数顺序 。
- Hindley-Milner类型推导 。 为了避免写参数和返回值的类型 , 结果给程序员写代码增加了很多的限制 。 为了让类型推导引擎开心 , 导致了很多完全合法合理优雅的代码无法写出来 。 其实还不如直接要程序员写出参数和返回值的类型 , 这工作量真的不多 , 而且可以准确的帮助阅读者理解参数的范围 。 HM类型推导的根本问题其实在于它使用unification算法 。 Unification其实只能表示数学里的“等价关系”(equivalence relation) , 而程序语言最重要的关系 , subtyping , 并不是一个等价关系 , 因为它不具有对称性(symmetry) 。
- 代数数据类型(algebraic data type) 。 所谓“代数数据类型” , 其实并不如普通的类型系统(比如Java的)通用 。 很多代数数据类型系统具有所谓sum type , 这种类型其实带来过多的类型嵌套 , 不如通用的union type 。 盲目崇拜代数数据类型的人 , 往往是因为盲目的相信“数学是优美的语言” 。 而其实事实是 , 数学是一种历史遗留的 , 毛病很多的语言 。 数学的语言根本没有经过系统的 , 全球协作的设计 。 往往是数学家在黑板上随便写个符号 , 说这个表示XX概念 , 然后就定下来了 。
- 智能手机市场|华为再拿第一!27%的份额领跑全行业,苹果8%排在第四名!
- 会员|美容院使用会员管理软件给顾客更好的消费体验!
- 行业|现在行业内客服托管费用是怎么算的
- 人民币|天猫国际新增“服务大类”,知舟集团提醒入驻这些类目的要注意
- 国外|坐拥77件专利,打破国外的垄断,造出中国最先进的家电芯片
- 技术|做“视频”绿厂是专业的,这项技术获人民日报评论点赞
- 面临|“熟悉的陌生人”不该被边缘化
- 中国|浅谈5G移动通信技术的前世和今生
- 页面|如何简单、快速制作流程图?上班族的画图技巧get
- 桌面|日常使用的软件及网站分享 篇一:几个动态壁纸软件和静态壁纸网站:助你美化你的桌面