【问题标题】:From Static Typing to Dynamic Typing从静态类型到动态类型
【发布时间】:2010-12-02 03:49:05
【问题描述】:

我一直致力于研究静态类型语言(C/C++、Java)。我一直在玩 Clojure,我真的很喜欢它。

我担心的一件事是:假设我有一个将 3 个模块作为参数的窗口,并且随着需求的变化,我需要将另一个模块传递给函数。我只是更改了函数,编译器在我使用它的任何地方都会抱怨。但是在 Clojure 中,直到调用该函数时它才会抱怨。我可以进行正则表达式搜索和替换,但似乎有机会错过一个呼叫,并且在实际调用该函数之前它会被忽视。大家是怎么处理的呢?

【问题讨论】:

  • 你们是怎么处理这个问题的?根据我的经验,正确的答案是“不太好”。

标签: java c++ ruby clojure type-systems


【解决方案1】:

这是自动化测试/测试驱动开发在动态类型语言中更为重要的原因之一。我没有用过 Clojure(我主要使用 Ruby),所以很遗憾我不能推荐一个具体的测试框架。

【讨论】:

  • +1。本质上,静态类型系统只是一组自动生成的单元测试(正如我的一个动态布道者朋友喜欢指出的那样)。您需要用动态语言手动编写这些代码,这为您提供了更大的灵活性,但也付出了更大的努力。由您和您的特定设计决定这种权衡是支持动态语言还是静态语言。
  • 您掩盖了处理静态类型系统需要付出努力的事实。问题是,哪个更大?
  • @jshen:但要付出多少努力?在 good 静态类型语言中,您几乎可以忘记它是静态类型的。只有在像 C 系列语言这样的垃圾中,您必须不断提醒编译器所有内容的类型。
【解决方案2】:

第一件事我想提一下,Bruce Eckel 写了一篇非常有趣的文章,名为 Strong Typing vs Strong Testing(不幸的是,该链接目前已关闭,但希望它会上线很快)。

他的想法是,在处理编译语言时,编译器只是充当自动测试的第一步。当转向动态语言时,您将失去第一级自动测试。但在这两种情况下,第一个自动级别只是测试的一部分,甚至不是非常重要的部分。

他的观点是,如果您正在正确地开发程序,即进行某种形式的测试和回归测试,那么缺少编译器只会迫使您添加更多的,有些基本的测试,这就是为什么它不大损失。

所以我想我给你的第一个答案是,专注于你的测试,这是你无论如何都应该做的事情,这样的改变不应该对你造成太大影响。

第二件事我想提一下的是,我见过的许多动态语言(例如 Python)具有更好的能力,可以在不破坏现有代码的情况下更改方法/类的功能。

例如,在 Python 中,如果您的方法过去接受两个参数,但现在需要第三个参数,则您始终可以添加一个默认参数而不会破坏任何现有代码,但您现在可以使用该参数。这是一种非常基本的技术,但在 Python 的情况下(我假设大多数其他动态语言也是如此),这些技术会变得更加有趣;由于它们是动态的,您几乎可以更改特定模块的函数实现,更改变量的含义等。

我建议看看 Clojure 有哪些技术可以实现类似的事情,并确定它们是否适用于您的情况。

【讨论】:

    【解决方案3】:

    如果该方法是您不是唯一用户的公共接口的一部分,则您会执行相同的操作。

    您添加一个带有额外模块的新方法,并更改旧方法以使用合适的默认值调用新方法。

    哦,如果你的程序那么大,请确保你有良好的测试(test-is 应该比 Java 更简单)

    【讨论】:

      【解决方案4】:

      测试覆盖率绝对重要。但是动态类型的语言将允许您以不同的方式工作。在强类型语言(如 Java)中,接口的更改需要修改所有调用者。在 Ruby 中,您可以这样做——但可能不会。相反,您可能会以几种方式之一为该方法添加灵活性。即:

      • 在 Ruby(与 Java 不同)中,很少有方法采用多达三个参数。因为您没有 Java 的强类型接口,所以您将问题分解为更小的部分和步骤。编写仅采用 1 个参数的方法更为常见,然后在变得更复杂时进行重构。
      • 在添加更多参数的同时保留旧的行为是可能的并且很常见。例如,如果您必须向双参数方法添加第三个参数,您将设置其默认值以保留旧行为(并为您节省重构)。如果您熟悉 jQuery 之类的 Javascript 库,它们会通过“可选”参数在任何地方利用这一点。
      • 与可选参数类似,方法可以扩展为采用灵活的参数列表。通过可靠的测试覆盖率,您可以很容易地向现有方法添加新行为,并安全地知道您没有破坏现有代码。在 Rails 中,“render”等方法有多种选择。

      【讨论】:

        【解决方案5】:

        Clojure 中并非完全没有编译器支持。在您给出的具体示例中,改变的是函数的数量,这将通过编译 Clojure 代码来获取。我仍在进行强 -> 动态类型转换,并觉得这很令人欣慰!

        【讨论】:

          【解决方案6】:

          当您迁移到动态语言时,您会失去一定程度的重构和类型安全性。编译器拥有的信息越多,它在编译时能为你做的事情就越多。

          【讨论】:

          • 请原谅我的无知,但动态类型如何让您失去某种程度的重构?
          • 他这里的意思可能是“自动重构”。
          • 我相信这句话的意思是“重构安全”。
          • 动态语言也需要较少的重构,因为您不必预先获得类本体。
          【解决方案7】:

          Tim Bray 讨论了它 here,Cedric 的评论是 here,还有一个 post 关于 artima 的详细讨论。

          【讨论】:

            【解决方案8】:

            如果你真的需要静态类型,你可以使用https://github.com/clojure/core.typed,它是 leiningen 模块来测试静态变量的传递。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-07-11
              • 2012-03-03
              • 1970-01-01
              • 2011-10-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-08-07
              相关资源
              最近更新 更多