静态结构和动态结构
工程师不应该如何看待技术问题
动态与静态打字是接缝吸引狂热者的对峙之一。 看到人们仅仅因为无法理解其他立场而倾向于强烈捍卫自己的一面,真让我难过。 我认为如果您不理解为什么静态和动态类型在某些情况下都很好的原因,那么您就是一个不完整的软件开发人员。 您选择只看图片的一部分。
工程师应该持开放态度:发表您的意见是可以的,但是当您不断抨击使数百万开发人员变得非常有生产力的想法时,您很可能会丢失一些东西。 尝试谦虚,可能最终会学到一些东西。
好吧那又怎样
我在使用Java或Haskell(从静态方面)或Python和Ruby(从动态领域)方面有很好的经验。 您无法获得Ruby代码的简洁性,它的静态编程语言元编程功能提供了惊人的可能性。 另一方面,当我不得不重构大量代码时,我真的最终会丢失静态类型。 测试有帮助(当它们在那里时),但通常还不够。
现在,有一些方法可以尝试充分利用整个世界。 一种解决方案是将语言用于系统的不同部分:也许您可以使用Java编写平台的基础结构,并使用JRuby编写业务逻辑。 为什么不?
但是,这意味着您在语言边界上仍然面临一些挑战。 您需要掌握几种工具并学习不同语言的最佳实践,等等。 因此,用一种语言获得两个世界的优势将是很有趣的。
妥协?
一个非常局部的解决方案是将静态类型与类型推断一起使用:它不能像动态类型语言那样为您提供相同的功能,但可以使您简洁。 这不是一个巨大的飞跃,但它有助于缩小差距。 类型仍在编译时进行计算,这意味着您可能必须简化解决方案,以便编译器也可以理解它。 但是,您不必键入所有类型。 同样,重构时,您必须在较少的地方更改类型。
类型推断听起来是个好主意,但它有缺点:尝试编写一些Haskell代码,很快您就会开始对从编译器派生的值获取类型感到困惑。 在实践中,我需要添加一些注释以使某些类型明确,否则我很快就会迷路。
结合静态类型和动态类型的另一种方法是从动态语言开始,并尝试使其更像静态。 有两个选项: 可选类型和结构性类型 。
基本上, 可选类型意味着您可以在动态语言的某些部分添加一些类型注释,以便可以静态检查其中的一部分,而其余部分将保持动态。 这有助于捕获错误并提高性能,因为它使编译器可以优化某些调用。 查看Typed Clojure和PEP -0484:python的类型提示 。
结构化类型意味着具有鸭子类型:您的类型是它支持的操作的列表。 基本上,您可以查看方法的代码,并查看对参数执行哪些操作。 它是否在参数栏上调用方法foo ? 很酷,这意味着在调用此方法时,我应确保bar具有某种类型,该类型具有方法foo 。 类别的名称无关紧要,重要的是其结构 (即其具有的字段和方法)。 原则上是个好主意,但是恕我直言,这可能很快就会使人困惑。
Scala支持它,您可以在其中编写以下代码:
def test(f: { def getName(): String }) {
log(f.getName)
}
但是看起来很冗长,不是吗? 通过结构类型推断,也许我们可以避免冗长的类型声明,并在编译时具有一定的安全性。 目前,我还不知道有哪种语言能正确解决这个问题。 有什么建议吗?
我能不能让Ruby和一个超级智能的编译器来捕获我的愚蠢错误?
不,到目前为止您还不能。
人们试图做到这一点:考虑一下Crystal语言,该语言旨在成为Ruby的静态类型版本或Mirah ,它试图成为JRuby的静态类型版本。 诱人的是,您可以拥有一种像Ruby这样的好语言,只需添加一点静态类型并获得两全其美。 问题在于,它不仅以这种方式起作用,就像Crystal的作者想出的那样。 最终,该特定功能最终会对语言的其他方面产生影响,并迫使您在全球范围内重新考虑它。
我认为在工程项目中经常会遇到这种情况:客户认为您可以挤入其他功能,而不会对安全性或体系结构完整性等其他方面产生影响。 作为工程师,我们必须打杂许多球,并在考虑所有这些因素的情况下做出决定。 再加一个球可能会迫使我们重新考虑战略,或导致所有球掉在地上。
静态结构和动态结构