从一个bug谈动态语言的隐形成本
Mocom   Sun May 22 2016 22:51:45 GMT+0800 (中国标准时间) [ 技术 ]     浏览次数:2133
版权声明: 本文发自http://mocom.xmu.edu.cn,为 赖永炫 老师的个人博文,文章仅代表个人观点。无需授权即可转载,转载时请务必注明作者。

这个网站是node+mongodb写的。刚开始入手写的时候很是舒畅:部署简单,代码简洁,数据扔到mongo的文档里,基本不需要考虑任何模式。编码,仿佛卸下了沉重的脚镣,一种解脱感。

可是,慢慢的,发现一些莫名的错误,很是百思不得其解。花了大量的时间来解决细节问题。这个过程中,也发现了几个非常典型的错误。按现在的流行说法,这几个都“坑”。每次学过新东西,都需要从这些坑踩过去。但无疑的,有些语言的坑比较多,有些坑比较少;有些坑前人已经告诫过你了;有些坑,你就是“前人”。

这里,举个简单的问题,是把文章或者blog存入mongodb,然后按时间逆序读取出来。一开始,我定义了一个blog的对象,赋值一个创建时间:

newBlog.time = Date();

代码没有报错,也很顺利的存进了据库的。但取出来显示的时候,发现blog无法按照时间来排序。查看mongodb的后台,显示的是:

"time" : "Sun Apr 10 2016 21:27:36 GMT+0800 (中国标准时间)"

时间似乎是对的,但为何没能进行排序呢?

我百度了(原谅我,也在用baidu)一些资料,觉得是存到monggodb的时间的格式不对。(据以前的开发经验,网页开发除了中文的编码问题,就是时间的格式问题了。) 用python写了个脚本,把现有的日期格式改成ISODate的格式:

"2011-12-20T07:22:50.836Z"

还把新插入的日期代码,改成这样:

newBlog.time = Date().toISOString();

发现可以排序了,貌似也运行无误,终于可以缓一口气了。

可是,诡异的是,过了一段时间,发现新加入的内容竟没有排在最前面!!问题原来还是没有解决!这次决定不用百度了^_^。几经挫折,又浪费了好多时间。

问题的答案,不得不回到mongodb的存储问题了。mongo以文档collection为基本的存储单元,无需定义所谓的数据模式schema,就可以往mongo的任何一个文档collection扔进去任何东西,包括字符串,数字,或者一个json对象。不巧的是,js也是一个对数据类型定义不严格的语言。虽然也有所谓的数据类型,但定义变量的时候,不会出现诸如Date today;这样的代码来标记数据的类型。当js+mongo+json进行开发的时候,真没有怎么想到还有数据类型这个概念,感觉一切都是字符串,一切都是文本。于是,悲剧出现了。你存到数据库里的是文本,那mongdo也按照文本给你排序了。

但其实mongodb虽然没有schema,但存储的时候,mongo还是会保存数据的类型。而这个类型,恰恰从插入的数据推导而来。

newBlog.time = Date();

newBlog.time得到的,居然是一个字符串!也就是说Date()给的字符串(这可能是js语言非常狗血的地方, 必须加一个new才能是一个对象。而这个对象,可以是mongodb中的一个带数据类型的字段。

newBlog.time = new Date();

至此,真相大白。但经过这个过程,你才发现,这个错误隐藏得如此之深,而网上基本无法找到类似的问题,也很难通过搜索检索到答案。假如我用的是c#、java这类的语言,或者我后台用的是传统的关系型数据库(如mysql),这个bug是没有机会出现的。它们严格的类型检查已经把这个问题过滤了。但只有javascript,monggodb,(动态语言+NoSQL数据库)让这些问题持久的存在,几乎到了生成阶段最后才被发现和解决。

原本想通过这个小例子,总结下静态语言和动态语言的优缺点。但网上一搜,发现有人已经写得非常清楚了:

不同的语言有不同的特点,同时也带来不同的优势。如果不能理解Scala的特点,就不可能知道如何运用Scala,以及发挥其最大的优势。一些语言有很显而易见的优势,也很容易理解,比如Python,Python的哲学(Zen of Python PEP 20 -- The Zen of Python**),我很早的时候曾经觉得有道理,尤其是One way to do it(一种方法做一件事情),理由是对任何任务,虽然可以采用很多方法,但总有最好的一种方法,通过在语言或者哲学层面这样定义后,能简化程序员的任务,从而达到提高效率的方法。但经过一段时间的思考后,我突然发现Python其实并不是“一种方法做一件事”的哲学,而是“一种方法做一百万件事情”的哲学:极其有限的数据结构(只有四个: List, Tuple, Dictionary, Sets),以及不能查看时间复杂度的访问方法,比如鼓励人们使用for x in list。

这种处理方式能达到Python最初的打算:发明一种每个人都能使用的简易语言,但是对于追求速度和效率的程序员而言,这几乎是略带噩梦性质的。当然,这不是说Python很慢,通过各种优化(比如NumPy/SciPy中的),以及Cython这样的将Python直接翻译为C/C++语言又重新通过C_Module方式读回Python环境的编译器,性能可以得到不少提升,但是仍旧,Python并不追求快。

再举一个语言的例子:Java。Java的特性或者优势何在?Java的第一个优势在于它是第一个系统提供模块化(module)设计的语言(在此之前有Smalltalk存在,该货是OOP的鼻祖)。在Java之前,炒程序员鱿鱼是很困难的事情,那些C/C++程序员,以及而且尤其是那些Lisp程序员,一旦炒掉他们,新来的人没有十天半个月,甚至半年,是不可能搞懂前任人士的代码的。每个人的代码有自己的逻辑,自己的思路,写上个数万行任谁来看都头疼。这也是为什么Paul Graham保罗·格雷厄姆(写了《黑客与画家》)讲他给雅虎做了一个用Lisp写成的在线商店的案例,在他离开后,雅虎根本没法维护他写的代码,因为数万行Lisp没人能弄得很清楚。

Java的模块化,给企业、大公司带来了第一道曙光,模块化之后,这些公司不再给程序员一整个任务,而是一大块任务的一小块。接口一定义,虚拟类一定义,换谁上都可以,管你是保罗·格雷厄姆这样的明星程序员,还是一个新来的大学生,程序员不听话就直接开除,反正模块化之后,开除程序员的成本大大降低,这也是为什么谷歌、甲骨文(这货最后收购了Java)一类的公司大规模的推崇Java,还一度提出了模块化人事管理的理念(把人当模块化的积木一样随时移进移出)。

过度企业化后,这延展出了Java的第二个特性,束缚手脚。保罗·格雷厄姆在《黑客与画家》中写道,Java属于B&D(捆绑与束缚)类型的语言。为何束缚手脚?因为要让新手和明星程序员写出类似质量的代码,尽可能的抹消人的才华对程序的影响。不同于C/C++,老手和新手写出的Java代码不会有上百倍的耗时差距。但同样也导致了Java的一个弱点——不容易优化。很多优化Java代码的程序员必须要对JVM(虚拟机)进行优化,实际上增大了很多任务难度。

以上是近期看到的帖子。近期在关注spark,也在看scala语言。推荐大家阅读:

Scala 是一门怎样的语言,具有哪些优缺点?


自动标签  : 动态   语言   成本   python   时间   java   程序员   模块化   问题   可以   发现   数据类型   定义   数据    

更多 [ 技术 ] 文章

请先 登录, 查看相关评论.