当前位置:首页 > 短网址资讯 > 正文内容

编程语言的动静之争:Clojure太灵活,我们该如何驾驭它?

www.ft12.com5年前 (2017-09-21)短网址资讯1548
作者|FT12短网址
编辑|短链接
编程语言的圣战,除了语言种类之分,也有动静门派之别。我们写着静态语言往往想着动态语言的灵活,写着动态语言又容易想着静态语言的稳定和可靠。常听到有人说,Clojure 确实优美,但动态语言实在驾驭不了,怎么办?

注:本文整理自 Morgan Stanley VP 何婧誉在 QCon 2017 北京站上的演讲,原题为:《属兔的处子——喜欢 Clojure,但怕动态语言太灵活怎么办》。

写在前面

古话说的好,静若处子,动若脱兔。这个我觉得非常适合形容动静态语言的区别,静态语言因为类型系统的关系,一直给人的是很稳定、很可靠,但是可靠到一定程度就变成了死板,会变成一个牢狱或者困住业务上所需的灵活性,因此常常需要很多层抽象,很多层胶水代码,代码就开始变得非常的晦涩,非常的难懂,而动态语言则完全是相反的。

常听到有人说,Clojure 确实优美,但动态语言实在驾驭不了啊!没有类型的帮助,在涉及到复杂的数据结构之后很容易失去对现有程序的理解,易读性也会急速下降,而这也确实是 Clojure 作为动态语言所造成的问题。但是部分解决这个问题的办法总是有的。core.typed 和 core.spec 两个核心库就可以帮助我们缓解动态语言太过野性框不住的问题,而本次演讲的任务就是向大家介绍这两个库,以及这两个库解决这一问题的不同角度。

静态语言 VS 动态语言

静态语言因为类型系统的关系,一直给人的是很稳定、很可靠,但是可靠到一定程度就变成了死板,会变成一个牢狱或者困住业务上所需的灵活性,因此常常需要很多层抽象,很多层胶水代码,代码就开始变得非常的晦涩,非常的难懂。动态语言则完全是相反的,所有东西都是从类型上来讲,以函数为例,灵活性已经足够了,但是通常我们写着写着就忘记数据长什么样子了,你可能今天写了一个函数说,输入一个函数的数据,然后过了一个星期之后,我已经完全忘记这个数据是什么东西了,因为生产环境里面,类型系统在没有编译器的帮助下,基本上都是一次性的,这个问题对于用户来说有相当大的困扰。

一直以来,这两派之间没有争出特别的高低,静态语言笑动态语言做不出大系统,动态语言笑静态语言写的太慢、废话太多,今天这个主题当然不可能解决这个纷争,但是希望通过 Clojure 这个语言可以给大家一些不太为人知的思路。马上就有人来问了,我写 Clojure 就是为了逃避这样的内容来写系统,这样灵活多好用啊,我想写什么就写什么,快速原型靠的就是这个,我非常同意这一点。

简单示例


在 Clojure 里面有一个 json,因为动态语言的关系相当的简单,完全没有废话。这个函数我觉得哪怕是不写 Clojure 的,这个也是应该很能读的懂的。首先有一个 Java 的 Reader,是 FileReader,这个 Reader 被传递到了这个 json 的函数里面,读出来文件内容,读到 Map 里面,但是读完之后,你知道数据长什么样吗?不知道,下次换一个 json 文件,同样的函数可以同样读,但是你不知道读出来是什么东西。讲到这里就已经有一点难度在里面了。

现在看一下,我现在读完了要处理,我处理之后,我写任意一个函数,如果说你不看这个函数写的什么东西,你知道它处理完成之后长什么样吗?不知道,你知道他希望这个 json 数据是什么样的形状吗?不知道。我现在看了代码之后,可以给你讲,它里面会有会有 Age、Name、Job、Address。


看一下 Age,它需要能够使用 Int,那应该是个整数,但是要看代码才知道,再下面还是简单,那你们觉得 Name 的值是什么东西?完全没有使用到,它是一个 String,它是不是姓和名放在一起了?还是放在一个 Vector 里面,可能姓和名是分开的,就是说不知道,要看代码才知道。


你看到代码之后觉得,原来是这样,它应该是一个 Vector,或者是 List,姓和名是分开放,因为它这系,它用空格来 Join 一下,这个是一个很浅显的例子,就已经说明了 Clojure 的动态灵活性非常强,但是也造成对数据的解释性标记不是很清楚。

前文是一个很浅显的例子,现在来看一个更具体的。为了这个主题我想了好几天,觉得还是写一个很小的项目来展示一下要讲的东西。那写什么东西呢?我又想了好几天,在此先谢谢链家。因为是这样的,既然要来北京,就要关注一下房价。我到网上去看二手房信息,但一页页翻过去很累,我不可能手写一个总结,于是就写点程序抓取。当然这里不是真的写了一个爬虫,只是抓几个页面做做样子,没有让链家服务受到伤害,请鸟哥放心。

命名空间做的基本上就是通过一个库把 html 读进来之后,进行一些简单的操作,把整理好的数据写到一个 EDN 文件里边。比如说第一条你可以看到这个小区 1150 万,三卧室两个客厅,一个厨房两个卫生间,包括面积之类的东西。再看这个数据转换的函数,它收到一个参数是 Page,但这个 Page 长什么样完全不知道。我是通过库读进来的,读进来之后,并不知道它长什么样子,现在看这个代码也很难知道,它到底会返回一个什么样的类型,什么样的数据,如果将来需要扩展的话,或者将来我要给另外一个人用,或者帮助另外的一个人去做一些扩展,做一些维护很难搞定。

这就是前文说的 Clojure 作为一个动态语言的弊端——太灵活。这个弊端导致经常会忘记函数的参数是什么样子,而且这个是小项目,项目一大,那就更麻烦。可能有人会说的,文档不就是做这个事情的吗?文档跟测试,没有紧密的联合在一起,文档本身的代码是剥离的,而相对代码本身是没有限制的。比如说很多代码上面会写,但是其实代码里面并没有,它可能起到的效果某种程度上也是挺有限的。

Core.typed

Core.typed 是一个类型系统。它和其他语言的类型系统还是有点不一样的地方,不同点在于它不是语言的一部分,而是一个即查即用的库。Lisp 的灵活性导致它能够作为一个库直接插进去,而不是要作为一个语言核心。因为它有宏,通过宏可以把一个很大的类型系统直接插进去,而且这个类型系统比一般的系统要灵活很多,主要体现在这几个方面:

  • 第一,它可以给已经写好的,没有标注过的,或者说是用的库里面没有标注过的函数直接加上类型;

  • 第二,不需要把所有函数全部加上类型,你不想要的话,就不需要;

  • 第三,你即使加上了也不一定要进行类型检查,所以它是一个选择性非常强的东西。它是为了能够和 Clojure 这样的语言进行协作。

那我们现在看一下它支持什么东西:

OptionType,现在很流行,这个流行的语言现在都有这个结构。

Ordered Intersection Type 这个我不多讲了,这个就是说一个函数,比如有两种参数形式,这两种参数类型可能又不一样,你再进行类型检查的时候,它会把这个参数从上到下有序的来进行一个匹配。unionType,写过 Haskell 人都知道,这个很简单,比如说整数,或者说是字符串,把它 union 一下,那就表示这个类型里面的东西可以是字符串,也可以是函数。

Identity 是很简单的函数,它会给你一模一样的东西,那它的类型是什么呢?它这个函数的类型是什么东西呢?让 Core.type 来帮我看一下。这个基本上可以看到前面有个 all,在这里对所有的 X 能取得的类型它返回的是一个 X,就是 Polymorphism 最简单的一个体现了。Occurrence Typing 这个东西见到的比较少,它是什么呢?它是通过检查代码里面写的控制流,比如像 if,或者像 switch,它能够进行类型推导。

举个例子,首先把这个 Form 绑到 A 这个名字上面,值就是 1,但是我把它标注成了 any,就是说这个 A,就算只是 1,然后再返回 A,这里大家觉得会返回什么东西?如果是检查一个类型,它最后返回的是 A,它是什么类型?Any,因为我已经标过了,我说 A 是 Any,所以它就相信 A 是 Any,但是如果我这么写,这个会返回什么东西?你可以看到它现在还是返回的是 A,这个 A 或者这个也是 A,那其他情况返回的是 Nil,那他现在觉得这个东西是什么呢?还是不是 Any,因为你现在有了控制流在这边,代码里已经写过了,所以它知道你只可能是 number,或者是 string,要不然就是 nil,所以最后 A 是 union string/number/nil。这个东西功能上是非常强大的,这个也是我强推的一个东西,这个你真正用起来就知道方便。

最后一个就是宏也会被展开之后再推导类型,宏跟大家刚刚知道的 switch 有点像,就是已经很直接了当的,告诉大家这个宏是可以展开之后判断类型。我做的 Demo 的 types Demo,就是把刚刚链家那个小项目加了类型系统,我现在是把它所制造的结果定义类型,但它其实是什么呢?是一个 Map。

Core.spec 总结

  • 通过一个库给动态语言加上类型系统——即插即用

  • 可以给已经写好的函数或者是用的无类型库标注类型

  • 可以选择性地加上类型

  • 加上了类型也并非一定要 type check

  • 支持 Option Type,Ordered Intersection Types, Union Types

  • 支持 Heterogenous Maps 和 Sequentials

  • 支持 Polymorphism (All, Context Bounds),Higher-Kinds

  • 支持 Occurrence Typing!(通过检查 control flow 进行类型推导)

  • 宏也会被展开后再推导类型

Core.spec

我本人很喜欢写这个,我觉得给函数加上类型非常过瘾,但是有问题,那有别的办法吗?有的,Core.spec,现在这个东西是 Clojure 核心,在很尽力地推广。在方法上或者在函数上,加上先限条件,功能要强大一点,强大在什么地方呢?

比方说生产环境,Runtime 不会受到影响,它的性能不会受到影响。因为如果你一天到晚在检验,它的性能上是会受到影响的,所以缺省验证是关闭掉的,如果你觉得某些东西可能重要性比较大,你要加上去也是可以的。spec 非常灵活,它可以把那种正则方式的 rule 给写起来,就是比如某个 list,我觉得里面开头至少有一个字符串,然后后面跟着的至少是 0 个的整数等等,你就可以用正则里面的加号,星号直接定义这个 rule。并且所有只有一个参数的 predicate 的函数统统可以跟它进行无缝对接,不需要另外语法把它转换成 spec。这里面有很多种的验证方式,那么多的验证的方式可能现在没有时间讲,就不讲了,总体来说就是可以把数据套在一个很灵活的模子里。

Core.spec 总结

  • Runtime 性能基本不会受到影响(缺省 spec 验证关闭)

  • Map 的类型应该就是 key 及其对应的值的类型!(keys)

  • Sequence 可以多方面限制(cat, alt, regex style matching, coll-of)

  • 只有一个参数的返回 boolean 值的函数通通都自动成为 predicate

  • 各种验证方式,满足你的需求 (conform, explain, valid?)

  • multi-spec 支持更复杂的数据结构

Core.type vs Core.spec

写在最后

core.typed 和 core.spec 你推荐哪个?

我的脑子喜欢 core.spec,因为有前景。我的内心喜欢 core.typed,因为给东西加类型写起来真得很过瘾。



扫描二维码推送至手机访问。

版权声明:本文由短链接发布,如需转载请注明出处。

本文链接:https://www.ft12.com/article_493.html

分享给朋友:

相关文章

FT12短网址:互联网生态未来将发展趋势解析

【FT12短网址资讯】这些年,业界对互联网生态的评论不断增加,在不少人看来,互联网生态不只会推进全部IT产业进入下一个黄金十年,更主要的是,它也将对IT产业格式带来主要的影响。简略来说,BAT这种格式是不是会完全被推翻,要害就在于互联网生态...

支付宝征战香港 海外版App支付宝HK即将来了

支付宝征战香港 海外版App支付宝HK即将来了

5月24日,支付宝宣告推出香港版电子钱包——支付宝HK,正式为香港居民供给无现金效劳。从前,香港人只能用支付宝在taobao买买买,不能像内地用户相同,走到哪儿都掏出手机,扫码付钱,但有了这个支付宝HK这个独立App以后,香港用户也将能用支...

曾估值30亿美金,如今公司只剩180人

也许很多人都不知道:凡客还活着。凡客这家公司从前多么张狂?最高估值达到过30亿美金!那时一天就会有500人入职。那是2010年,凡客具有超越1.3万名职工,曾具有30多条商品线,商品触及服装、家电、数码、百货等全领域,当年卖出了3000多万...

短网址和二维码的应用:从零开始实现一套聚合支付系统

短网址和二维码的应用:从零开始实现一套聚合支付系统

短网址和二维码在移动扫码支付领域应用越来越广泛,FT12短网址的小编出门从来不带钱,现在从吃饭、交通、购物,甚至是取款机取款,都已经不需要钱和银行卡了。那么这么流行的二维码扫码支付,是如何实现的呢?其实聚合支付的原理很简单,长网址生成短网址...

HTTP的长连接和短连接

HTTP的长连接和短连接

 本文总结&分享网络编程中涉及的长连接、短连接概念。    关键字:Keep-Alive,并发连接数限制,TCP,HTTP一、什么是长连接     HTTP1.1...

18年前比尔·盖茨的15大预言 如今基本全都实现了

北京时间 5 日早间消息,据美国《商业内幕》(Business Insider)报道,早在 1999 年,微软(68.17, -0.76, -1.10%)创始人比尔-盖茨曾写下一本名为《未来时速-数字系统与商务新思维》(Business@...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。