最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 聊一聊函数式编程中的pointfree

    正文概述 掘金(夜还不够黑丶)   2020-12-28   562

    前言

    熟悉pointfree我认为是很好的一件事情,也是编程中的重要的风格之一。因为它可以帮助你在正确的抽象层次上进行思考。基于这个初衷写了这片文章。

    如果你学会了这种编程风格,并不是说就应该在任何地方都采用pointfree。我更希望你以一种拓展思维的角度去看这个事情。

    什么是pointfree

    Pointfree这个概念,在编程中叫做隐式编程,同时也叫做无点编程(point-free),其概念是函数定义不用标示所需要操作的参数(点)的一种编程范式。

    pointfree 则是将函数与函数之间以无点的方式组合起来的思想。这里的复合方式比如compose、sequence,pipe等方式。

    所以在严格使用复合模式使用的时候,非常适配于方程式推理。可以先想像成可以组合的数学公式。并且很神奇的依然可以套用我们数学中的一些定理。

    使用pointfree的前提

    在函数式编程中使用pointfree的前提是纯函数之间,至少说“建议”是纯函数,如果你可以把副作用控制的足够好的话也是可以的。

    结合上一篇文章中的内容,我们可知函数是没有副作用的 - 每当输入参数一样时函数返回值是恒定一致的

    那么这个时候,我们面临着如何更加优雅的组合纯函数的这个问题

    pointfree解决了什么问题

    先看一个命令式的例子:

    聊一聊函数式编程中的pointfree 可以先看到上面抽离出来的三个纯函数,抽离的非常不错,就像我们上一篇文章说的,我们需要更多这样的纯函数,具有非常好的确定性。

    operate过程操作一组数据 a, b。在operate内部写了三个命令,先进行相乘,然后乘积自增1,最后自乘。operate过程会返回最后的结果squared。

    我相信这是谁都写过的代码,这也是我们项目代码组成的基本缩影。首先说明,这种写法是没有问题的,不但没问题,如果你像上面的例子一样:该抽离的函数抽离,命令过程一步一步的很清晰,那么最终效果是非常好的。

    既然已经那么好了,我们这里是要解决什么问题呢?--- 内存开辟问题。

    我们可以看到上放例子,operate过程里面有三条指令,每条指令运行的结果都赋值给了一个变量。声明变量的话系统会单独开辟一块内存来存储,而且存储空间是跟数据量成正比的。那么我们可不可以避免这三个变量的声明呢?

    诶,这里有人可能有疑问:“我可以一个变量都不声明,我完全可以这么写”。

    聊一聊函数式编程中的pointfree

    这样写是可以计算出相同的结果,但是这样写首先就违反了命令式的初衷,没有简明的体现出执行步骤,代码不清晰,不易懂,扩展性还不如上方的例子。如果添加更多的过程,数括号都要数一段时间。

    这个时候,一些函数工具解决了我们这个问题,下面将通过一些现有的javascript函数库中的函数来讲几个例子。

    pointfree的实现方式有哪些

    这里先介绍两个库

    • Ramda 非常实用的javascript函数库
    • lodash-fp 柯里化的lodash库

    R.pipe

    Ramda.pipe 【汉译pipe: 管道】

    先看一下pipe如何执行我们的上方例子:

    聊一聊函数式编程中的pointfree

    如果对比看一下,pipe的代码实现方式非常的简洁,执行过程依然清晰,从左往右执行。而且我们可以很方便的修改顺序以达到修改执行过程的目的。

    注意: pipe 函数的结果不是自动柯里化的。


    Node.js中的pipe

    在Node.js中也有一个函数叫做pipe,在Node.js中pipe函数负责处理流数据(stream),例如

    聊一聊函数式编程中的pointfree

    pipe前面是读取流,后面是写入流。在实际项目中我们在Nodejs发起请求的时候,request则为读取流,response为写入流。

    这里还是补充一下request场景下的pipe代码,以方便理解下方为何pipe可以避免无用“点”的增加:

    聊一聊函数式编程中的pointfree

    一个读写流通过pipe处理之后看上去十分简洁、利落。

    Node.js中的pipe的作用根我们要说的pointfree中的无点概念很像,大家可以想一下,一个接收流的过程,我们往往需要定义一个变量,当数据量很大的时候,系统需要随之相对应的开辟较大的内存,为了更优雅的处理这样的场景,就用到了pipe去进行读写流操作。

    当Node.js想用一个管道函数去达到消除变量的目的的时候,这个思想根函数式编程中的“无点”想法非常一致。都是打算通过管道思想去消除“点”。

    如果你对Node.js的pipe感兴趣的话,有位作者还写了一个可控制读写流速度的pipe,附上传送门Nodejs中流的操作,实现简单的pipe

    R.compose

    Ramda.compose 【汉译compose:组成】

    我们先来看下compose如何优雅的实现上面的例子。

    聊一聊函数式编程中的pointfree

    执行顺序变了,pipe执行顺序是从左到右,compose的执行顺序是从右至左。从执行方式和结果上来看,这两种方法除了执行顺序的不同,其它都一样。

    比较官方的说法是compose针对的是嵌套执行的场景:

    聊一聊函数式编程中的pointfree

    其时看过compose源码就会发现,把arguments顺序reverse后顺序就可以反转,但是为什么没有这么做呢?这块比较有意思:

    作者认为上方嵌套执行场景为“右倾”,为了解决右倾,compose选择了“左倾”,并认为“左倾”更加能够反映数学上的含义。

    从右至左执行符合传统嵌套的阅读顺序,因为你是由外往内读代码的。square -> addOne -> multiply。我认为大可不必深究这个问题,可以根据你主观的判断来选择使用compose或者pipe。如果你知道他们之间的具体使用区别,欢迎在评论区留言。


    Redux中的compose

    用过Redux状态管理的同学应该都知道,在Redux为数不多的6个API中,有一个compose。Redux中的compose一般用法如下。

    1、增强store(官方)

    聊一聊函数式编程中的pointfree

    2、装饰组件 (非官方)

    聊一聊函数式编程中的pointfree

    上方两个场景中,compose内部也是pointfree,消除了重复的“点”,达到组合的目的,依然简洁明了。

    那么问题来了,Redux中的compose与Ramda中的compose有什么区别?

    答:没有区别。

    Redux就是原封不动的把人家函数库的compose方法放到了自己的API中,原因是: 聊一聊函数式编程中的pointfree 因为方便...没有错,就是因为方便。你可见compose这个方法那是相当的好用了。redux只有2kB(包括依赖),可是它依然把compose方法放到源码中,你可见comopse的重要性。

    我打算以后的写等SDK等也借鉴一下,以防你没有compose,我自带一个给你用,用了我的compose使得我的api调用更加简洁。

    Diy

    也完全可以自己进行组合,不依赖第三方库。只需要符合pointfree风格即可。

    pointfree的特性有哪些

    严谨来说是管道模型或者洋葱模型的特性,因为考虑到一个问题,就像上面提到的,其实是可以通过嵌套方式来实现pointfree风格的。是因为那种方式不易读等缺点被列为非主流写法。所以在整理特点的时候偏向于管道模型。函数式编程的模型后面单独写一篇文章聊一聊。

    易读

    通过上面立举的嵌套模型和pointfree的例子可以清晰的看到,pointfree的执行过程是非常易读的。其实在编程语言中也一直在避免嵌套的情况。比如回调地狱,promise的链式调用,条件语句嵌套等等,我们都会想一些相应的解决方法来解决嵌套的情形,使得代码更加简洁。

    易调试

    很久以前我看见有人在质疑函数式编程的调试很难,其实恰恰相反,pointfree的调试非常方便,甚至可以称为它的优点之一。

    在这里要复习一下我在《聊一聊函数式编程中的数学》中写到的单位元概念。我们可以利用单位元在pointfree中进行调试。

    换句话说,在数学中,恒等函数为函数f(x) = xf(x)=x,输入等于输出。

    我们依然用上方pointfree的例子,来看一下如何打印日志。

    聊一聊函数式编程中的pointfree

    上述print函数可以插入你想要插入的任何位置。很方便的在执行过程中进行调试。

    当然Ramda也提供了单位元函数:Ramda.Tap

    聊一聊函数式编程中的pointfree

    如下使用:

    聊一聊函数式编程中的pointfree

    方程推理

    在第一段中提到过这样一句话,复合模式使用的时候,非常适配于方程式推理,类似于数学公式的组合。

    形式上,一个在集合S上的二元运算 被称之为可结合的若其满足下面的结合律。

    pointfree

    如果对这方面感兴趣可以看我以前写的一篇文章《聊一聊函数式编程中的数学》

    复用性

    我们放心的去“拼接”函数,就像拼接乐高模型,无论怎么拼接,他始终都是乐高,不会拼成其它材质的东西。纯函数之间的拼接后,依然是纯函数。

    所以在不同的使用场景中,我们可以选择不一样的“零件”进行组合。纯函数的特性也让复用性大大提升,你完全可以放心使用拆解和组合出来的函数。

    易扩展

    就像上面我们可以在组合执行过程中插入R.tap打断点一样,我们不光可以在中间打断点,还可以插入一些辅助函数帮助我们更好的处理计算逻辑。

    类似Either,safeProps,Maybe,IO等等,很方便的进行扩展。

    END

    从搭建一栋建筑角度来说,并不是整个建筑都用一种材料才是合适的,哪里该用钢筋,哪里该用砖头都是需要合理搭配的。我们该抽离成pointfree风格使用,还是使用命令式过程,都需要去合理的搭配。

    这种风格会延伸出来两种模型 管道模型、洋葱模型,下一篇文章来聊一聊。中间可能有一些Flutter和小程序底层架构的文章可能会在中间穿插。大前端方面的知识体系也积累蛮多,打算沉淀文字一波。

    ?

    往期文章推荐

    • 《聊一聊函数式编程中的副作用概念》
    • 《聊一聊函数式编程中的Hindley-Milner》
    • 《聊一聊函数式编程中的数学》

    起源地下载网 » 聊一聊函数式编程中的pointfree

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元