简体   繁体   English

Go的优点和缺点是拒绝未使用的依赖项

[英]Pros and cons of Go rejecting unused dependencies

Google's new language Go tries to make dependencies management easier by explicitly requiring that all dependencies listed in a module actually be used . Google的新语言Go试图通过明确要求实际使用模块中列出的所有依赖项来简化依赖项管理。 The compiler will reject a module that declares a dependency to a module without using anything from that module. 编译器将拒绝向模块声明依赖关系的模块,而不使用该模块中的任何内容。

It is illegal for a package to import itself or to import a package without referring to any of its exported identifiers. 包导入自身或导入包而不引用其任何导出的标识符是非法的。

I can think of some obvious advantages (eg cleaner modules) but maybe there are some non-obvious ones. 我可以想到一些明显的优点(例如更清洁的模块),但也许有一些非显而易见的优点。 The only disadvantage I can think of is having an overly pedantic compiler, complaining too much during refactoring, but maybe there are more? 我能想到的唯一缺点是有一个过于迂腐的编译器,在重构过程中抱怨太多,但也许还有更多?

Do you have any experience with other languages enforcing this? 您是否有其他语言执行此操作的经验? What are the pros and cons of this approach? 这种方法的优点和缺点是什么?

Not only you need to explicitly use all dependencies, but also all variables must be used. 您不仅需要显式使用所有依赖项,还必须使用所有变量。 The compiler will give you errors when you have unused variables. 当您有未使用的变量时,编译器会给您错误。

They are annoying. 他们很烦人。 But it will make others happy because they get clean code. 但它会让别人高兴,因为他们得到了干净的代码。

I'm thinking that probably Go designers intended Go to be a language that is largely IDE dependent. 我想可能Go设计师打算成为一种主要依赖于IDE的语言。

I'm guessing that the biggest motivation for this, and the biggest result, is an improvement in compile times. 我猜这个最大的动机和最大的结果是编译时间的改进。 The tech preview video made a major point of their ability to compile large amounts of code in short time periods (their example was 200,000 lines of code in 8 seconds on a MacBook - no machine specs given). 技术预览视频突出了他们在短时间内编译大量代码的能力(他们的例子是在MacBook上8秒内有200,000行代码 - 没有给出机器规格)。

Also in the tech video, they specifically mention that one of the biggest ways they achieved that was in changing the way modules were compiled and linked. 同样在科技视频中,他们特别提到他们实现的最大方法之一就是改变模块编译和链接的方式。

Here's an example of how something would work in a current C/C++ system: 这是一个在当前C / C ++系统中如何工作的例子:

Class A is defined in header file C_H and implemented in C_CPP. A类在头文件C_H中定义,并在C_CPP中实现。 Class B derives from C and is implemented in files B_H and B_CPP. B类源自C,并在文件B_H和B_CPP中实现。 Class A derives from B and is implemented in files A_H and A_CPP. A类派生自B,并在文件A_H和A_CPP中实现。

Because of the derivation chains, A_CPP includes A_H, B_H, and C_H. 由于推导链,A_CPP包括A_H,B_H和C_H。 B_CPP includes B_H and C_H. B_CPP包括B_H和C_H。 C_CPP includes C_H. C_CPP包括C_H。 Due to the nature of the C preprocessor, which essentially turns a #include into a cut and paste operation, the contents of C_H are run through the compiler 3 times and B_H is run through twice. 由于C预处理器的性质,它基本上将#include转换为剪切和粘贴操作,C_H的内容通过编译器运行3次,B_H运行两次。

Further, the contents of A_CPP, B_CPP, and C_CPP all live in their own object files. 此外,A_CPP,B_CPP和C_CPP的内容都存在于它们自己的目标文件中。 Hence when the linker goes to resolve Ao, it is forced to load and process both Bo and Co Also, when it resolves Bo, it has to process Co again. 因此,当链接器去解析Ao时,它被强制加载并处理Bo和Co。此外,当它解析Bo时,它必须再次处理Co。

Precompiled headers can help quite a bit with the first part of this problem, but they also can be a royal pain to maintain and I know a lot of developers who simply don't use them for that reason. 预编译的头文件可以帮助解决这个问题的第一部分,但是它们也可能是一个很难维护的问题,而且我知道许多开发人员因为这个原因而根本不使用它们。 It also doesn't fundamentally change the problem - the headers are still processed at several levels multiple times, only now a precompiled binary is processed instead of the source. 它也没有从根本上改变问题 - 标题仍然在多个级别处理多次,现在只处理预编译的二进制文件而不是源代码。 Several steps are cut out, but not the entire process. 切出几个步骤,但不是整个过程。

Go approaches things differently. 以不同方式处理事情。 In the words straight out of the PDF from their tech talk : 他们的技术谈话直接从PDF中得出的话

"The Go compiler pulls transitive dependency type info from the object file - but only what it needs. If A.go depends on B.go depends on C.go: - compile C.go, B.go, then A.go. - to compile A.go, compiler reads Bo not Co At scale, this can be a huge speedup." “Go编译器从目标文件中提取传递依赖类型信息 - 但只提供它需要的东西。如果A.go依赖于B.go取决于C.go: - 编译C.go,B.go,然后是A.go. - 编译A.go,编译器读取Bo not Co大规模,这可能是一个巨大的加速。“

OK, slight tangent is done. 好的,完成了轻微的切线。 Why is that relevant? 为什么这有关系? The answer is also in the Go Tech Talk PDF: 答案也在Go Tech Talk PDF中:

"Package model: Explicit dependencies to enable faster builds." “包模型:显式依赖关系,以实现更快的构建。”

I'm guessing that the Go developers took the stance that when compile times are measured in seconds, even for very large projects, that it's more productive for developers to keep compile times that short. 我猜测Go开发人员认为,当编译时间以秒为单位测量时,即使是非常大的项目,开发人员编译时间缩短也会更有效率。 Say it takes me 8 seconds to compile 200,000 lines of code and discover that I've got an extraneous package import, 5-10 seconds (with a good IDE, or good familiarity with your dev environment) to find it and fix it, and another 8 seconds to recompile. 说我需要8秒钟来编译200,000行代码并发现我有一个无关的包导入,5-10秒(有一个好的IDE,或者熟悉你的开发环境)来找到并修复它,并且另外8秒重新编译。 Call it 30 seconds total - and now all of my future compiles stay in the 10 second range. 称它总共30秒 - 现在我所有未来的编译都保持在10秒范围内。 Or we can let our module grow by including unnecessary dependencies, and watch that compile time creep up from 8 to 10, 12, or 15 seconds. 或者我们可以通过包含不必要的依赖项来让我们的模块增长,并观察编译时间从8秒增加到10秒,12秒或15秒。 It doesn't seem like much because we're all used to compile times on the order of minutes - but when you start to realize that that's a 25% performance degradation, you stop and think about it for a minute. 它看起来并不多,因为我们都习惯于在几分钟的时间内编译时间 - 但是当你开始意识到性能下降25%时,你会停下来思考一下。

Go compile times are already lightning fast. 编译时间已经很快了。 Consider also that processor speeds are still increasing (if not as much as in the past) and that the number of cores available is also increasing (and compiling large amounts of code is well suited to multi-threading). 还要考虑处理器速度仍然在增加(如果没有那么多),并且可用内核数量也在增加(并且编译大量代码非常适合多线程)。 200,000 lines of code in 8 seconds today means that it's not unreasonable to imagine 200,000 lines of code compiling essentially instantaneously in 10 years. 今天8秒内有200,000行代码意味着想象在10年内基本上即时编译200,000行代码并不是不合理的。 I think the Go team has made a conscious decision here to make compile times a thing of the past, and while the issue you bring up is only a very small part of that, it is still a part of that. 我觉得围棋队取得了在这里有意识的决定,使编译时间的过去的事情,而你提出这一问题的仅是很小的一部分,但它仍然其中的一部分。

On another note entirely, the Go team also seems to have developed a philosophy of a language design that forces some good programming practices. 另一方面,Go团队似乎也开发了一种语言设计理念,这种理念强制实施一些优秀的编程实践。 To their credit, they've made the effort to achieve that without serious performance penalties, and largely succeeded. 值得赞扬的是,他们已经努力实现这一目标而没有严重的性能损失,并且基本上取得了成功。 [Aside: The only two things I can think of offhand that actually impact performance are garbage collection and forcibly initialized variables - and the latter is pretty trivial in this day and age.] This is going to royally irritate some programmers, while making others happy. [旁白:我能想到的另外两件事实际影响性能是垃圾收集和强制初始化变量 - 后者在这个时代非常微不足道。]这会让一些程序员感到恼火,同时让别人开心。 It's an old, old argument in the programming world, and it's pretty clear which side Go has come down on, like it or not. 在编程世界中,这是一个古老而古老的论点,而且很明显Go的哪个方面已经下降了,无论喜欢与否。

I think the two forces together influenced their decision, and I think that at the end of the day it's a good way to go, although I support other commenters here who have suggested allowing a "--strict" flag or some such to make this particular behavior optional, particularly during the early stages of a project's lifecycle. 我认为这两种力量共同影响了他们的决定,我认为在一天结束时这是一个很好的方式,虽然我支持这里的其他评论者建议允许“ - 严格”标志或其他一些这样做特定行为是可选的,特别是在项目生命周期的早期阶段。 I could easily see myself defining variables or including packages when I first start writing code that I know I will need later, even though I haven't yet written the code that needs them. 当我第一次开始编写我知道稍后需要的代码时,我可以很容易地看到自己定义变量或包含包,即使我还没有编写需要它们的代码。

As mentioned by yuku, if you have got an IDE that is on par with what Netbeans and Eclipse can do for java, you don't really have to care about this sort of thing. 正如yuku所提到的,如果你有一个与Netbeans和Eclipse可以为java做同样的IDE,那么你真的不必关心这类事情。

Right click on the little light bulb in the margin and select "Remove all unused dependencies". 右键单击边距中的小灯泡,然后选择“删除所有未使用的依赖项”。

For variables that aren't used, they usually get a squiggly underline, and are pretty easy to spot. 对于未使用的变量,它们通常会得到一个波浪形的下划线,并且很容易被发现。

The only difference here is that unlike other languages, you actually have the compiler complaining in addition to the IDE but if you use an IDE anyway, this becomes a non-issue. 这里唯一的区别是,与其他语言不同,除了IDE之外,你实际上还有编译器抱怨,但如果你使用IDE,那么这就成了一个问题。

At work we have coding policies that pretty much state that we have to do the same thing (ourselves), among other things, for other languages of course. 在工作中,我们的编码政策几乎表明我们必须做同样的事情(我们自己),当然还有其他语言。 So I would say that this sort of this does actually have real applications. 所以我会说这种确实有真正的应用程序。 Though IMHO, the compiler should give the developer the option to toggle this behaviour on and off. 虽然恕我直言,编译器应该让开发人员选择打开和关闭此行为。 Strict mode anyone? 任何人都严格模式?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM