`
lgh06
  • 浏览: 55145 次
文章分类
社区版块
存档分类
最新评论

动态语言已死?

 
阅读更多

动态语言已死?

Published:19 Dec 2014Category:其它

真相总会不期而遇。它们总是不经意间降临,譬如当我读到这条微博的时候:

这是关于Facebook的Flow的一个很不错的讨论 – http://t.co/5KTKakDB0w — David J. Pearce (@whileydave) November 23, 2014

David是Whiley编程语言的作者,这门语言内建了许多静态类型检查的特性,它比较小众,但粉丝还不少。它的一个很有意的特性就是流敏感(flow sensitive)类型(有时也被称为流类型),当它与联合(union)类型配合使用的时候会比较有用。下面是从它的使用向导中摘录的一个例子:

function indexOf(string str, char c) => null|int:
 
function split(string str, char c) => [string]:
  var idx = indexOf(str,c)
 
  // idx has type null|int
  if idx is int:
 
    // idx now has type int
    string below = str[0..idx]
    string above = str[idx..]
    return [below,above]
 
  else:
    // idx now has type null
    return [str] // no occurrence
   

记住了,像Ceylon这样的语言也支持流敏感类型,甚至是Java在也在一定程度上也是支持的,因为Java也有联合类型!

try {
    ...
}
catch (SQLException | IOException e) {
    if (e instanceof SQLException)
        doSomething((SQLException) e);
    else
        doSomethingElse((IOException) e);
}

Java的流敏感类型是显式且拖沓的。我们当然希望编译器能推导出所有的类型。像下面这么写的话也会进行类型检查并且能够通过编译就好了:

try {
    ...
}
catch (SQLException | IOException e) {
    if (e instanceof SQLException)
        // e is guaranteed to be of type SQLException
        doSomething(e);
    else
        // e is guaranteed to be of type IOException
        doSomethingElse(e);
}

流类型或者说流敏感类型指的是编译器可以从当前程序的控制流中推导出唯一可能的类型。它是在像Ceylon这样的现代语言中才出现一个相对较新的概念,它使得静态类型变得异常强大,尤其是当语言本身能支持通过var或者val关键字来进行复杂的类型推导的时候。

配备了Flow之后的静态类型的JavaScript

我们回到David的那条微博并看一下这篇文章对Flow是如何评价的:

http://sitr.us/2014/11/21/flow-is-the-javascript-type-checker-i-have-been-waiting-for.html

由于length的取值可能为空,因此Flow就不得不在函数体内判断它是否为空值。下面是进行了类型检查的版本:
function length(x) {
  if (x) {
    return x.length;
  } else {
    return 0;
  }
}
 
var total = length('Hello') + length(null);

Flow能够推导出在if体内x不能为空。

这相当巧妙。微软的TypeScript中也有一个类似的新特性。但Flow与TypeScript不同(至少它是这么声称的)。从官方的Flow介绍中可以看到Facebook Flow的本质所在:

Flow的类型检查是可选的——你不再需要在代码中到处进行类型检查了。然而,Flow的底层设计是基于这么一个假设的,即大多数JavaScript的代码其实都是静态类型的;尽管很多时候代码中并没有出现明确的类型,但在开发人员的脑海中它是实际存在的。Flow会尽可能地自动推导出这些类型,这意味着你无需修改代码便能找出里面的类型错误。换句话说,一些严重依赖于反射的JavaScript代码,尤其是框架的代码,通常很难进行静态检查。对于这种骨子里就是动态类型的代码,类型检查就变得不太准确了,因此Flow提供了一种简单的方式来显式地将这些代码置为是可信的,并忽略它们。这个设计在Facebook海量的JavaScript代码库中得到了验证:许多代码都默认归到了静态类型的分类,开发人员无需显式标注这些代码的类型便能找出其中类型错误的问题。

个中三昧

绝大多数JavaScript代码都是隐式的静态类型的

再进一步

JavaScript代码都是隐式的静态类型的

没错!

类型系统深受程序员的喜爱。他们喜欢正式地声明数据的类型,使得这些数据处于一个比较窄的约束下,这样才能确保程序的正确性。这正是静态类型的本质所在:一个设计良好的数据类型更不容易出错。

人们也喜欢将自己的数据结构以一种规范的形式存储到数据库中,这就是为什么SQL如此大行其道的原因,而无schema的数据库的市场份额始终上不去。这其实本质上都是一样的。在无schema的数据库中,其实你的脑子里还是存在一个schema,只是没有进行类型检查而已,并增加了确保程序正确性的负担。

还有一点需要注意的:一些NoSQL的厂商拼了命地在发表这些荒谬不堪的言论,告诉你,其实你根本就不需要schema,其实只是为了给自己的产品找定位,不过这种营销的伎俩很容易识破。无schema与动态类型的真正需求其实,都相当的少。换言之,你上一次在Java程序中通过反射来调用方法是什么时候的事了?很少用到吧。

不过有一样东西是以前静态类型语言所不具备而动态类型语言能做到的:避免代码冗长。这是因为虽然程序员钟爱类型系统以及类型检查,但他们并不喜欢去敲这些代码。

代码冗长才是问题所在,而非静态类型

来看一下Java进化的历史吧:

Java 4
List list = new ArrayList();
list.add("abc");
list.add("xyz");
 
// 为什么我需要这个Iterator呢?
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    // 好吧,我清楚我这里就是String类型,为啥还要类型转换?
    String value = (String) iterator.next();
 
    // [...]
}
Java 5
// 我居然得声明两次泛型!
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("xyz");
 
// 比之前好多了,不过还是得声明是String类型
for (String value : list) {
    // [...]
}
Java 7
// 进步了点
List<String> list = new ArrayList<>();
list.add("abc");
list.add("xyz");
 
for (String value : list) {
    // [...]
}

    // [...]
}
Java 8
// 终于进化成了这样,虽然姗姗来迟了
Stream.of("abc", "xyz").forEach(value -> {
    // [...]
});

顺便提一下,当然了,上述这个功能其实用Arrays.asList()就能完成了。

Java 8还远谈不上完美,但至少是日臻完美了。现在在lambda参数列表中不用声明类型的原因是编译器替我们完成了推导,这点是相当重要的。

看一下Java 8之前类似于这个lambda的话要怎么写(假设那会儿已经有Stream了):

// Yes, it's a Consumer, fine. And yes it takes Strings
Stream.of("abc", "xyz").forEach(new Consumer<String>(){
    // And yes, the method is called accept (who cares)
    // And yes, it takes Strings (I already say so!?)
    @Override
    public void accept(String value) {
        // [...]
    }
});

现在我们拿Java 8跟JavaScript的版本作一个比较:

["abc", "xyz"].forEach(function(value) {
    // [...]
});

在简洁性方面它几乎已经达到和函数式,动态类型的JavaScript一样的水准了,唯一的不同之处就在于,我们(以及编译器)知道这个value的类型是String。还有就是我们知道forEach方法的确存在。我们还知道的是forEach方法接受一个带单个参数的函数。

最后貌似可以得出如下的结论:

像JavaScript与PHP这样的动态类型语言之所以流行的原因是因为它们"能跑起来"。你不需要学习经典静态类型语言里面所有复杂的语法(想想Ada和PL/SQL吧!)。你马上就可以开始写代码了。程序员知道哪些变量包含字符串,因此没有必要写下来。这没错,的确是没有必要什么都写出来。

看一下Scala(或者C#,Ceylon,以及几乎任意的现代编程语言):

val value = "abc"

它除了能是字符串,还能是什么吗?

val list = List("abc", "xyz")

这个除了List[Stirng],还能是别的类型吗?

不过请注意了,如果你需要显式声明变量类型的话,也是可以的——总会有那么些个边缘的用例:

val list : List[String] = List[String]("abc", "xyz")

不过绝大部分的语法都是“可选”的了,它们能由编译器来完成推导。

动态类型语言已死

讲的所有这些的结论就是,一旦语法的冗长及阻碍从静态类型语言中刨掉之后,那么使用动态类型语言就完全没有任何优势了。编译器已经相当快了,部署也很快速。只要使用了合适的工具,静态类型检查所带来的好处是巨大的。(不相信?请读下这篇文章)。

举个例子,SQL也是一门静态类型语言,它的使用障碍主要是语法造成的。没错,很多人都认为它是一门动态类型语言,因为他们是通过JDBC来访问SQL的,比方通过一些无类型的SQL语句的字符串拼接而成。如果你写过PL/SQL,Transact-SQL或者jOOQ写过嵌入式SQL的话,你绝对不会认为SQL是动态类型的,你马上就会感谢PL/SQL,Transact-SQL,以及你的Java编译器给你的SQL语句所做的类型检查了。

那么,让我们摒弃这个由我们一手创造出来的东西吧,因为我们实在是太懒了,不想在代码里声明这些类型。让敲代码变得更愉快吧!

如果你是Java语言的专家组成员,并碰巧看到了这篇文章,请你一定要把var和val关键字,以及流敏感类型添加到Java语言中。我保证一定会爱死你的!

原创文章转载请注明出处:动态语言已死?

英文原文链接

分享到:
评论

相关推荐

    tombstone:带有墓碑PHP死代码检测:headstone::zombie:

    scheb /墓碑 ... 该库为您提供了一个工具箱,用于在代码中放置,跟踪和评估逻辑删除。... 一段时间后,日志会告诉您哪些墓碑已死,哪些墓碑未死(所谓的“吸血鬼”)。 安装 该库由多个组件组成,需要独立安装和配

    phpdcd:死代码检测器(DCD)用于PHP代码

    局限性由于PHP是一种非常动态的编程语言,因此由phpdcd执行的静态分析无法识别使用以下语言功能之一执行的函数或方法调用: 反射API call_user_func()和call_user_func_array() 带有变量类名的new运算符的用法静态...

    patterns:模式和模式语言。 工作正在进行中。 阅读,复制,混音,分享

    建立克里斯托弗·亚历山大(Christopher Alexander)定义的模式语言和生成序列。 除非另有说明,否则所有作品均根据CC-BY-SA许可。 什么是模式? 模式是经过验证的解决方案,可以解决在某些情况下发生的重复出现的...

    JAVA基础课程讲义

    String类的常用方法(已讲过,不再讲!) 120 StringBuffer和StringBuilder 121 String和StringBuffer和StringBuilder使用要点 123 时间处理相关类 124 Date时间类(java.util.Date) 124 DateFormat类和...

    SpringBoot-vue:一个示例演示基础SpringBooot,带有vueJS2.x + webpack2.x作为Java全栈Web实践

    当人们引用Java时,它非常肿,过时,并且彼此之间的某些开发效率比其他动态语言要低。 甚至在有人大声喊叫之前,“ Java已死”。 但这是真的吗? 实际上,如果您长时间长时间关注Java,您的感觉就会太深。 尽管它有...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    / 41 2.5 本章小结 / 42 第3章 垃圾收集器与内存分配策略 / 43 3.1 概述 / 43 3.2 对象已死? / 44 3.2.1 引用计数算法 / 44 3.2.2 根搜索算法 / 46 3.2.3 再谈引用 / 47 3.2.4 生存还是死亡? / 48 3.2.5 ...

    View Rendered Source-crx插件

    视图源已死 了解浏览器如何呈现页面,而不仅仅是服务器发送的内容。 轻巧的Chrome扩展程序,向您展示浏览器如何将页面的原始HTML构造(呈现)为正常运行的DOM,包括JavaScript所做的修改。 对于使用Angular,ReactJS...

    Visual Studio Help Downloader Plus v5.0.0.0

    1、修复dev14(Visual Studio 2015)加载语言时挂死(该bug由“远景论坛”的“铁浪”提交,在此表示感谢), 修改为默认加载"en-us"语言,如果没有该语言则显示提供的第一种语言; v3.0.0.1(2014.05.28日)更新内容 1、...

    基于位置服务的图书馆座位预约管理系统.zip

    基于LBS图书馆座位资源动态管理系统 开发语言 微信小程序 主要有三个模块:首页,预约记录,我的。 先有个登陆界面,登陆进入首页,首页基于LBS位当前设备位置,下面显示附近的三个图书馆和与当前设备的距离,写死...

    计算机课程设计 户籍管理系统(论文)

    随着时代的发展、科学的进步,信息时代已经遍布全球,计算机已广泛地深入各行各业,起着越来越巨大的作用。随着人口的不断增长,管理这些庞大的数据是非常困难的,因为人口的信息是随时变化的,因此必须对人口信息...

    网格计算环境下工作流关键技术的研究

    网格计算是网络计算、分布式计算以及高性能计算领域中研究的重点和必然的 发展趋势,而网格工作流是网格计算中的...功能的扩展有向无环图建模语言能对动态工作流模型进行定义。遥感图像处理应用 已成功地应用于该平台。

    Visual Studio Help Downloader Plus v5.0.1.0

    1、修复dev14(Visual Studio 2015)加载语言时挂死(该bug由“远景论坛”的“铁浪”提交,在此表示感谢), 修改为默认加载"en-us"语言,如果没有该语言则显示提供的第一种语言; v3.0.0.1(2014.05.28日)更新内容 1、...

    c语言编写单片机技巧

    因此,使用C语言进行程序设计已成为软件开发的一个主流。用C语言来编写目标系统软件,会大大缩短开发周期,且明显地增加软件的可读性,便于改进和扩充,从而研制出规模更大、性能更完备的系统。 综上所述,用...

    超级有影响力霸气的Java面试题大全文档

    内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...

    java 面试题 总结

    内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的...

    Java 虚拟机面试题全面解析(干货)

    ·支持动态语言; 支持try-with- resources 引入 Java nio.2开发包; ·数值类型可以用2进制字符串表示,并且可以在字符串表示中添加下划线; 钻石型语法; nu值的自动处理。 Java 8 函数式接口 Lambda表达式 接口的增强 ...

    疯狂JAVA讲义

    1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6 1.3.2 Java程序的...

    TrueCrypt的国内延伸版本CnCrypt V1.11(单一文件绿色版)

    (密匙文件支持:任何类型的文件)电脑上那么多文件,要是想尝试暴利破解,就等着累死吧。 8.3支持PKCS 11运行库,可以使用硬件口令卡(例如:U盾)进行加密认证。密钥随身携带,从而再次提高了数据的安全性,以此...

    Java虚拟机

    3.2 对象已死吗 3.2.1 引用计数算法 3.2.2 可达性分析算法 3.2.3 再谈引用 3.2.4 生存还是死亡 3.2.5 回收方法区 3.3 垃圾收集算法 3.3.1 标记-清除算法 3.3.2 复制算法 3.3.3 标记-整理算法 3.3.4 分代...

    C#开发经验技巧宝典

    0938 追加查询结果到已存在的表 547 0939 利用对多个表中的字段创建新记录集 547 0940 利用EXECUTE执行SQL语句 548 第20章 数据库技术 549 20.1 Access数据库的使用 550 0941 如何为Access数据库设置密码...

Global site tag (gtag.js) - Google Analytics