动态语言有真正的优势吗?[关闭]


29

首先,我想说Java是我曾经使用过的唯一语言,所以请原谅我对此主题的无知。

动态类型的语言允许您将任何值放在任何变量中。因此,例如,您可以编写以下函数(伪代码):

void makeItBark(dog){
    dog.bark();
}

您可以在其中传递任何值。只要该值具有bark()方法,代码就会运行。否则,将引发运行时异常或类似情况。(如果我对此有误,请纠正我)。

貌似,这为您提供了灵活性。

但是,我阅读了一些有关动态语言的文章,人们说的是,在以动态语言设计或编写代码时,您会考虑类型并将其考虑在内,就像使用静态类型语言一样。

因此,例如,在编写makeItBark()函数时,您打算让它只接受“可能吠叫的东西”,而您仍然需要确保仅将这些东西传递给它。唯一的区别是,当您犯错时,编译器现在不会告诉您。

当然,此方法有一个优点,那就是在静态语言中,要实现“此函数接受任何可能吠叫的东西”,您需要实现一个显式Barker接口。不过,这似乎是次要的优势。

我想念什么吗?通过使用动态类型的语言,我实际上可以获得什么?


6
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof"))。该参数甚至都不是,而是一个名为tuple的匿名名称。鸭子输入(“如果它发出嘎嘎声……”)使您可以进行具有零限制且无语法开销的临时接口。您可以使用Java之类的语言来执行此操作,但最终会产生很多混乱。如果Java中的函数需要ArrayList,而您想给它另一个收集类型,那么您就是SOL。在python中甚至无法出现。
Phoshi 2014年

2
以前曾问过这种问题:这里这里这里。具体而言,第一个示例似乎可以回答您的问题。也许您可以改写以使其与众不同?
logc 2014年

3
请注意,例如在C ++中,您可以拥有一个模板函数,该模板函数可以与具有bark()方法的任何类型T一起使用,并且当您传递错误时,编译器会抱怨,而不必实际声明包含bark()的接口。
威伯特

2
@Phoshi Python中的参数仍必须为特定类型-例如,它不能为数字。如果您有自己的对象临时实现(通过某些自定义getMember函数来检索其成员),makeItBark则会因为您调用dog.bark而不是而大放异彩dog.getMember("bark")。使代码起作用的原因是每个人都暗中同意使用Python的本机对象类型。
2014年

2
@Phoshi Just because I wrote makeItBark with my own types in mind doesn't mean you can't use yours, wheras in a static language it probably /does/ mean that.如我的回答所指出,通常情况并非如此。Java和C#就是这种情况,但是这些语言的类型和模块系统十分残缺,因此不能代表静态类型可以做什么。我可以写一个完全通用的makeItBark几个静态类型语言,甚至非功能性的像C ++或D
Doval

Answers:


35

动态类型的语言是单类型的

比较类型系统,动态类型没有优势。动态类型是静态类型的一种特殊情况 -它是一种静态类型的语言,其中每个变量都具有相同的类型。你可以通过使每个变量是类型的实现在Java中(零下简洁)同样的事情Object,并具有“对象”值类型Map<String, Object>

void makeItBark(Object dog) {
    Map<String, Object> dogMap = (Map<String, Object>) dog;
    Runnable bark = (Runnable) dogMap.get("bark");
    bark.run();
}

因此,即使没有反射,您也几乎可以在任何静态类型的语言中实现相同的效果,并且不使用语法上的便利。您没有任何其他表达能力;相反,你有更少的表达能力,因为在一个动态类型语言,你否认限制变量对某些类型的能力。

用静态类型的语言制作鸭皮

而且,一种好的静态类型语言将允许您编写与任何具有bark操作的类型一起使用的代码。在Haskell中,这是一个类型类:

class Barkable a where
    bark :: a -> unit

这表达了这样的约束:对于某些类型a被认为是Barkable,必须存在一个bark函数,该函数采用该类型的值并且不返回任何值。

然后,您可以根据Barkable约束条件编写通用函数:

makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)

这表示makeItBark它将满足任何类型Barkable的要求。这似乎类似于interface在Java或C#,但它有一个很大的优势-类型没有指定了前面,他们满足哪种类型的类。我可以说这种类型DuckBarkable任何时候,即使Duck是我没有写过的第三方类型。实际上,作者Duck没有编写bark函数也没关系-当我告诉Duck满足条件的语言时,我可以在事后提供它Barkable

instance Barkable Duck where
    bark d = quack (punch (d))

makeItBark (aDuck)

这表示Ducks可以吠叫,而它们的吠叫功能是通过在鸭子发出嘎嘎声之前先对其进行打孔来实现的。这样一来,我们就可以呼吁makeItBark鸭子了。

Standard ML并且OCaml更加灵活,因为您可以通过多种方式满足同一个类型类。在这些语言中,我可以说可以使用常规排序对整数进行排序,然后转而说它们也可以按除数进行排序(例如,10 > 5因为10可被5整除)。在Haskell中,您只能实例化一次类型类。(这使Haskell可以自动知道可以调用bark鸭子了;在SML或OCaml中,您必须明确要使用哪个 bark函数,因为可能有多个。)

简明

当然,在语法上存在差异。您提供的Python代码比我编写的Java代码简明得多。实际上,简洁是动态类型语言魅力的重要组成部分。但是通过类型推断,您不必显式地编写每个变量的类型,从而使您可以编写与静态类型的语言一样简洁的代码。静态类型的语言还可以为动态类型提供本机支持,从而消除了所有强制类型转换和映射操作(例如C#dynamic)的冗长性。

正确但类型错误的程序

公平地说,即使类型检查器无法验证,静态类型也必须排除某些技术上正确的程序。例如:

if this_variable_is_always_true:
    return "some string"
else:
    return 6

if即使else分支永远不会发生,大多数静态类型的语言也会拒绝该语句。在实践中,似乎没有人使用这种类型的代码-对于类型检查器来说,任何太聪明的事情都可能使您的代码的未来维护者诅咒您和您的近亲。举例来说,有人成功地将4个开源Python项目转换为Haskell,这意味着他们没有做任何好的静态类型语言无法编译的事情。而且,编译器发现了一些与类型相关的错误,而单元测试没有发现这些错误。

我看到的关于动态类型的最强论据是Lisp的宏,因为它们允许您任意扩展语言的语法。但是,类型化拍子是具有宏的Lisp的静态类型化方言,因此似乎静态类型化和宏并不是互斥的,尽管可能很难同时实现。

苹果和橙子

最后,不要忘了语言之间的差异不仅限于类型系统。在Java 8之前,实际上不可能用Java 进行任何类型的功能编程。一个简单的lambda将需要4行样板匿名类代码。Java也不支持集合文字(例如[1, 2, 3])。工具(IDE,调试器),库和社区支持的质量和可用性也可能存在差异。当某人声称在Python或Ruby中比Java更有生产力时,需要考虑该功能差异。比较包含所有电池语言核心类型系统的语言是有区别的。


2
你忘了你的属性源的第一款- existentialtype.wordpress.com/2011/03/19/...

2
@Matt Re:1,我没有认为这并不重要;我在简洁下解决了这个问题。回复:2,尽管我从未明确说过,但“好”一词的意思是“具有全面的类型推断”和“具有一个模块系统,允许您在事实之后匹配代码以键入签名”,而不是像Java / C#的接口。关于第3条,举证责任在于您向我解释,如何给定两种具有相同语法和功能的语言,一种是动态类型的,而另一种具有全类型推断的,那么您将无法在两种语言中编写相同长度的代码。
Doval 2014年

3
@MattFenwick我已经证明了它的正确性-鉴于两种语言具有相同的功能,一种是动态类型的,另一种是静态类型的,它们之间的主要区别是类型注释的存在,而类型推断则将其消除。语法上的任何其他差异都是肤浅的,功能上的任何差异都使比较成为苹果还是橘子。由您来说明此逻辑是如何错误的。
Doval 2014年

1
您应该看看Boo。它使用类型推断进行静态类型化,并具有允许扩展语言语法的宏。
梅森惠勒2014年

1
@Doval:是的。顺便说一句,lambda表示法并不是专门用于函数式编程:据我所知,Smalltalk具有匿名块,Smalltalk尽可能地面向对象。因此,通常的解决方案是传递带有一些参数的匿名代码块,而不管这是一个匿名函数还是一个仅具有一个匿名方法的匿名对象。我认为这两种构造从两个不同的角度(功能性和面向对象的一种)表达了基本上相同的想法。
Giorgio

11

这是一个困难且相当主观的问题。(而且您的问题可能会基于意见而关闭,但这并不意味着它是一个不好的问题-相反,即使考虑此类元语言问题也是一个好兆头-它仅不适合问答形式该论坛)。

这是我的观点:高级语言的目的是限制程序员对计算机的处理。这使许多人感到惊讶,因为他们相信这样做的目的是为用户提供更多功能并实现更多成就。但是由于您用Prolog,C ++或List编写的所有内容最终都将作为机器代码执行,因此实际上不可能赋予程序员比已经提供的汇编语言更多的功能。

高级语言的目的是帮助程序员更好地理解他们自己创建的代码,并使他们在做同一件事时更有效率。子例程名称比十六进制地址更容易记住。自动参数计数器比调用序列更易于使用,在这里您必须在没有帮助的情况下完全正确地获取参数数量。类型系统会进一步发展,并限制您可以在给定位置提供的参数类型

这是人们的看法不同的地方。有些人(我当中有一些人)认为,只要您的密码检查例程始终期望恰好有两个参数,并且始终是一个字符串,后跟一个数字ID,则在代码中声明该变量并在出现以下情况时自动提醒您很有用您以后忘记遵循该规则。将这种小规模的簿记工作外包给编译器,可以帮助您解放更高层次的精力,并使您更好地设计和架构系统。因此,类型系统是一个制胜法宝:它们让计算机完成自己擅长的事情,而人类则完成自己擅长的事情。

其他人则有很大不同。他们不喜欢编译器告诉该怎么做。他们不喜欢决定类型声明和键入它的额外的前期工作。他们更喜欢探索性的编程风格,在这种风格中,您编写实际的业务代码而没有一个计划可以准确告诉您在哪里使用哪些类型和参数。对于他们使用的编程风格,这可能是真的。

当然,我在这里过于简化。类型检查并不严格地与显式类型声明联系在一起;还有类型推断。使用实际上可以接受各种类型参数的例程进行编程,确实可以实现完全不同且非常强大的功能,否则这些操作将是不可能的,只是很多人不够专心和一致,无法成功使用此类余地。

最后,这种不同的语言都非常流行并且没有消失的迹象表明,人们在编程方面大相径庭。我认为编程语言的功能主要与人为因素有关-哪些因素可以更好地支持人为决策过程-只要人们的工作方式截然不同,市场就会同时提供非常不同的解决方案。


3
感谢您的回答。您说过,有些人不喜欢编译器告诉该怎么做。[..]他们更喜欢探索性的编程风格,在这种风格中,您编写实际的业务代码而没有一个计划可以准确告诉您在哪里使用哪些类型和参数。这是我不了解的事情:编程不像即兴演奏。在音乐中,如果您打错了音符,听起来可能很酷。在编程中,如果将某些内容传递给原本不应该存在的功能,则很可能会遇到讨厌的错误。(继续下一条评论)。
Aviv Cohn 2014年

3
我同意,但很多人同意。人们对自己的心理成见非常占有欲,尤其是因为他们通常不知道它们。这就是为什么有关编程风格的辩论通常会演变为争论或争斗的原因,而在互联网上以随机的陌生人开始进行辩论几乎没有用。
Kilian Foth,2014年

1
这就是为什么-根据我的读物来看-使用动态语言的人会同时考虑使用静态语言的人的类型。因为在编写函数时,它应该采用特定类型的参数。编译器是否强制执行此操作无关紧要。因此,归结为静态类型可以帮助您实现这一点,而动态类型则不能。在这两种情况下,函数都必须接受特定种类的输入。所以我看不到动态类型的优点是什么。即使您喜欢“探索性编程风格”,也仍然无法将所需的任何内容传递给函数。
Aviv Cohn 2014年

1
人们经常谈论非常不同类型的项目(尤其是关于规模)。与完整的ERP系统相比,网站的业务逻辑将非常简单。发生错误的风险较小,而能够非常简单地重用某些代码的优势更为相关。假设我有一些代码可以从数据结构生成Pdf(或一些HTML)。现在,我有了一个不同的数据源(首先是来自某些REST API的JSON,现在是Excel导入器)。在像Ruby这样的语言中,“模拟”第一个结构,“使其吠叫”并重用Pdf代码非常容易。
thorstenmüller2014年

@Prog:动态语言的真正优势是在描述静态类型系统很难实现的事情时。例如,python中的一个函数可以是一个函数引用,一个lambda,一个函数对象,或者上帝知道什么,它们都将以相同的方式工作。您可以构建一个包装另一个对象的对象,并以零的语法开销自动分派方法,并且每个函数本质上都具有神奇的参数化类型。动态语言可让您快速完成工作。
Phoshi 2014年

5

使用动态语言编写的代码未与静态类型系统耦合。因此,与不良/不足的静态类型系统相比,这种耦合的缺乏是一个优点(尽管与大型静态类型系统相比,这可能是一种洗礼或不利条件)。

此外,对于动态语言,不必设计,实现,测试和维护静态类型的系统。与具有静态类型系统的语言相比,这可以使实现更简单。


2
人们难道最终不会在其单元测试中重新实现基本的静态类型系统(当针对良好的测试范围时)?
2014年

另外,这里的“耦合”是什么意思?它如何在例如微服务架构中体现?
2014年

@Den 1)好问题,但是,我认为这超出了OP和我的回答的范围。2)我的意思是从这个意义上讲耦合;简而言之,不同类型的系统会对用该语言编写的代码施加不同(不兼容)的约束。抱歉,我无法回答最后一个问题-我不了解微服务在这方面有什么特别之处。

2
@Den:非常好的观点:我经常观察到我用Python编写的单元测试掩盖了编译器以静态类型语言捕获的错误。
Giorgio

@MattFenwick:您写道,“ ...对于动态语言,不必设计,实现,测试和维护静态类型系统是一个优势。” Den发现,您经常必须直接在代码中设计和测试类型。因此,工作量并没有减少,而是从语言设计转移到了应用程序代码。
Giorgio
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.