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

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

www.ft12.com7年前 (2017-09-21)短网址资讯1942
作者|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短网址:人工智能最先应用的十大行业

[ 短网址资讯 ] 5月25日,王明耀宣布了主题为《联想之星人工智能出资规划》的讲演。他初次发表了联想之星在人工智能范畴出资组合,体系阐释了联想之星在人工智能范畴的出资规划。联想之星在人工智能范畴出资62个项目,散布在10大职业,...

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

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

人生两件事:修炼定力,扩展心量

人生两件事:修炼定力,扩展心量

自然环保是术,心灵环保是道。既要在术上做文章,更要在道上下功夫。双管齐下,才能从根本上解决环境污染问题。升起过,发光过,温煦过,这就够了;云遮过,风吹过,雨淋过,又能怎样?旭日就是旭日。对未知世界,我一是好奇,二是恐惧。因为好奇,我有探索未...

office高级诀窍,让你提早下班

office高级诀窍,让你提早下班

【短网址诀窍】Office中有很多诀窍,会的人几分钟搞定,不会的人可能要消耗N个小时。这一长一短之间,差就差在对一些作业神技的掌握。当然某些神技可能躲藏得过深,而有些纯粹即是被咱们疏忽了!那些被你疏忽过的作业神技  1. 表格标题跨页重复 ...

FT12短网址教你如何记录阅读进度

大家是否会遇到这些需求:如果有三个tab页签,从某个tab页签下跳出去打开新页后,点击浏览器后退键,能回到跳出去的相应tab页签下希望像原生app那样在wap端的列表页跳到详情页,点击浏览器后退键,能回到跳出去的列表处如果有上拉加载更多,希...

关于短网址算法的讨论和分析

今天小编无意中在互联网上看到了关于短网址一些算法,非常新奇,和已有的算法有很大的区别:  1)将长网址md5生成32位签名串,分为4段, 每段8个字节;  2)对这四段循环处理, 取8个字节, 将他当作16进制串与0x3fffffff(30...

发表评论

访客

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