关于动态与静态键入的不便之处

有时会有这些关键时刻。 它们完全出乎意料地发生,例如当我阅读此推文时:

David是鲜为人知但一点也不有趣的Whiley编程语言的作者 ,该语言内置了许多静态类型检查。 Whiley语言最有趣的功能之一是流敏感类型化(有时也简称为流类型化),与联合类型一起使用时,它最有用。 入门指南中的示例

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的流敏感类型是显式和冗长的。 我们可以期望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之类的现代语言中,它是一个相对较新的概念,它使静态键入变得异常强大,尤其是如果该语言还支持通过varval关键字进行复杂的类型推断!

使用Flow进行JavaScript静态输入

让我们回到David的Tweet,看看这篇文章对Flow的评价:

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

使用带有null参数的length的存在会通知Flow,该函数中应该进行null检查。 此版本会进行类型检查:

function length(x) {
  if (x) {
    return x.length;
  } else {
    return 0;
  }
}

var total = length('Hello') + length(null);

Flow能够推断if主体内x不能为null

真是狡猾。 在Microsoft的TypeScript中可以观察到类似的即将发布的功能 。 但是Flow与TypeScript不同(或声称有所不同)。 Facebook Flow的精髓可以从Flow官方公告中看到:

Flow的类型检查是可选的-您无需一次对所有代码进行类型检查。 但是,在Flow设计的基础上,假设大多数JavaScript代码是隐式静态类型化的。 即使类型可能不会出现在代码中的任何地方,但开发人员还是将它们视为推理代码正确性的一种方法。 Flow会尽可能自动地推断出这些类型,这意味着它可以发现类型错误,而根本不需要对代码进行任何更改。 另一方面,某些JavaScript代码(尤其是框架)大量使用了反射,而这些反射通常很难静态地进行推理。 对于这种固有的动态代码,类型检查将不太精确,因此Flow提供了一种简单的方法来明确信任此类代码并继续前进。 这种设计已通过我们在Facebook上庞大JavaScript代码库得到了验证:我们的大多数代码都属于隐式静态类型类别,在此类别中,开发人员可以检查其代码中是否存在类型错误,而不必用类型明确注释该代码。

让它沉入

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

再次

JavaScript代码是隐式静态类型的

是!

程序员喜欢打字系统。 程序员喜欢正式地对自己的数据类型进行推理,并将其置于狭窄的约束中以确保程序正确。 这就是静态类型化的全部本质:减少由于设计良好的数据结构而引起的错误。

人们还喜欢将其数据结构以设计良好的形式放在数据库中,这就是为什么SQL如此流行并且“无模式”数据库将不会获得更多市场份额的原因。 因为事实上,这是同一回事。 您仍然可以在“无模式”数据库中拥有一个架构,只是不进行类型检查,因此就剩下了保证正确性的全部负担。

jooq在Java中写SQL的最佳方法

附带说明:显然, 一些NoSQL供应商一直在写这些荒唐的博客文章来拼命定位他们的产品,声称您确实根本不需要任何架构,但是很容易看出这种营销手段。 对无模式的真正需求与对动态类型的真正需求一样罕见。 换句话说,您最后一次编写Java程序并通过反射调用每个方法的时间是什么时候? 究竟…

但是有一件事,过去静态类型语言没有,而动态类型语言却具有:避免冗长的手段。 因为尽管程序员喜欢类型系统和类型检查,但是程序员却不喜欢键入(就像在键盘上键入一样)。

非静态输入

考虑一下Java的发展:

Java 4
List list = new ArrayList();
list.add("abc");
list.add("xyz");

// Eek. Why do I even need this Iterator?
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    // Gee, I *know* I only have strings. Why cast?
    String value = (String) iterator.next();

    // [...]
}
Java 5
// Agh, I have to declare the generic type twice!
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("xyz");

// Much better, but I have to write String again?
for (String value : list) {
    // [...]
}
Java 7
// Better, but I still need to write down two
// times the "same" List type
List<String> list = new ArrayList<>();
list.add("abc");
list.add("xyz");

for (String value : list) {
    // [...]
}
Java 8
// We're now getting there, slowly
Stream.of("abc", "xyz").forEach(value -> {
    // [...]
});

在旁注中,是的,您可能一直都使用Arrays.asList()

Java 8仍然远远不够完美,但是事情越来越好。 我最终不必再在lambda参数列表中声明类型的事实,因为它可以由编译器推断出来,这对于提高生产率和采用率确实很重要。

考虑一下Java 8之前的lambda等效项(如果之前有Streams):

// 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那样的冗长程度( 我真的不介意Java中那些缺少的列表和映射文字 ),唯一的不同是我们(和编译器) 知道 value是类型String 。 而且我们知道 forEach()方法存在。 而且我们知道 forEach()接受一个带有一个参数的函数。

归根结底,事情似乎可以归结为:

动态类型化语言(如JavaScript和PHP)之所以流行,主要是因为它们“刚刚运行”。 您不必学习经典的静态类型化语言所需的所有“繁重”语法(只需考虑一下Ada和PL / SQL!)。 您可以开始编写程序。 程序员“ 知道 ”变量将包含字符串,因此无需写下来。 没错,无需写下所有内容!

考虑一下Scala(或C#,Ceylon,几乎是任何现代语言):

val value = "abc"

除了String以外,还能是什么?

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

除了List[String]以外,还可以是什么?

请注意,如果需要,您仍然可以显式键入变量-总是存在以下情况:

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

但是大多数语法是“选择加入”的,并且可以由编译器推断出来。

动态类型语言已死

所有这些的结论是,一旦从静态类型的语言中删除了语法上的冗长和摩擦,使用动态类型的语言绝对没有任何优势。 如果使用正确的工具,则编译器的速度非常快,部署也会很快,并且静态类型检查的好处是巨大的。 ( 不相信吗?请阅读本文

例如,SQL还是一种静态类型的语言,其中很多摩擦仍然是由语法造成的。 但是,许多人认为它是一种动态类型的语言,因为他们通过JDBC(即通过无类型的串联SQL语句字符串)访问SQL。 如果您使用jOOQ用Java编写PL / SQL,Transact-SQL或嵌入式SQL ,您将不会想到这种SQL,您会立即意识到您的PL / SQL,Transact-SQL或Java编译器将对所有SQL语句进行类型检查。

jooq在Java中写SQL的最佳方法

因此,让我们放弃我们创建的混乱局面,因为我们懒得键入所有类型(双关)。 打字愉快!

而且,如果您正在阅读Java语言专家组成员的内容,请务必将varval以及对流敏感的类型添加到Java语言中。 为此,我们将永远爱你!

翻译自: https://www.javacodegeeks.com/2014/12/the-inconvenient-truth-about-dynamic-vs-static-typing.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值