11 ms·
A theory of modern Golang
- transitorykris 9y agoI've recently been bit by global state that used function initializers (func init() isn't the only way to execute logic when a package is imported!). The package used very reasonable defaults, but panic()'d when things didn't look right. So, forking, maintaining patches until upstream changes the behavior, painful.
- kornish 9y ago> func init() isn't the only way to execute logic when a package is imported! What are other ways to execute logic when a package is imported? init() is the only one I'm aware of.
- transitorykris 9y agopackage globals var ( X = blah() ) func blah() int { return 123 }
- weberc2 9y agoI agree with every bit of this, especially with constructors taking their dependencies explicitly. In fact, I try to write my Go code in such a way that constructors aren't needed (sometimes this can't be helped) so the "constructor" is simply a type initialization expression `foo := Foo{Bar: 7}`. This keeps everything simple and testable.
- axaxs 9y agoAbsolutely agreed on the ridding of constructors. Offer the option to set things (eg the Logger), and if it's nil, use some defaults. This seems much cleaner than making a huge function signature. The net/http package does this really well.
- ninkendo 9y agoAre you going to do the nil check in every member function though? I run into this problem with Go a lot, actually. (I'd like to make initialization of a some member variable optional, but checking if it's nil every time I use it becomes a pain. Not all types have useful zero values either... I'm looking at you, map.)
- axaxs 9y agoWell, in my cases I usually add an unexported 'check()' method, which is generally called by every other method that needs state. It would basically just be something of x.Z == nil {x.Z = DEFAULTZ}
- chubot 9y agoIn other words, use a pure dependency-injection style, and ZERO globals. I'm writing Python and some C++, and I have been doing this for maybe 5-6 years. It takes a little practice to structure your programs like this, but once you know how, it has a lot of benefits. It's not really idiomatic in those communities either. Python and C++ Libraries use globals all the time, although I think it's becoming less common. I guess Java programmers have been doing dependency injection for even longer. It probably became popular more than 10 years ago. But I'm not really sure how "pure" the Java ecosystem is. Singletons are banned under this style. Instead, a singleton is just an object you instantiate once in main(), and pass throughout the rest of your program. I think the problem with Java is lack of free functions. Static methods confuse things too. The pure-dependency injection style is basically functional programming, requiring state as an explicit parameter.
- wickawic 9y agoIf you are using spring (or any of the less cumbersome DI frameworks) this is exactly true. Generally in Java DI frameworks you make a bunch of factory methods for your logic classes(this may be magically done through annotations). A singleton in this approach is just a factory that always returns the same object.
- chubot 9y agoWhat I don't really understand is why you need a DI framework in Java. I use this style Python all the time with zero frameworks. You just make the objects in main() and make the objects in your test main(). Use helper functions if necessary. I do think Spring was very influential because I remember other people in the mid-2000's talking about testing, dependency injection, and Spring. I recall lots of people using Guice at Google but I never used it since I did very little Java. I feel like there is something about Java where you need a DI framework, and in Python you don't. I don't feel like you need one in C++ either. Or it might not even be possible to write one. I think LLVM is written in C++ in a style with zero globals. It's a library-based design.
- deleted 9y ago
- mourner 9y agoExpected to see an article about the development of modern Go the board game theory after the advent of AlphaGo and AI generally. Got disappointed. Happens every time :(
- 0x264 9y agoMe too :(
- vorg 9y agoPerhaps blog posters on the Go language need to refer to it as Golang in titles, etc.
- iends 9y agoWhy? The name of the language is "Go" and not "Golang". "Golang" is a search engine hack.
- vorg 9y ago> "Golang" is a search engine hack Although "Go" is the name of the language, "Golang" can also be a submission title hack so as not to confuse those who "Expected to see an article about the development of modern Go the board game theory".
- SwellJoe 9y agoClarity has value. "Golang" in the title provides more information to the reader at no cost. Why not use "Golang"?
- dang 9y agoOk, have a 'lang'.
- sagichmal 9y agoNot cool. That's not the name of the language or the article. Please change it back.
- linkmotif 9y ago> "modern Go" The language was first released in 2007 :). Man...
- chubot 9y agoIt was developed starting around 2007 but it wasn't released / open-sourced until late 2009.
- deleted 9y ago[deleted]
- DAddYE 9y agoThis isn't "modern go" but more the "standard" in any language.
- weberc2 9y agoSadly not in Python or JavaScript or C++. But yes, definitely good ideas for any language.
- JBReefer 9y agoThis is basically "Use DI, and move towards SOLID" Well, yeah! Those are great principles in any language - I find it concerning (and maybe indicative of something) that the Go community has voted this up. These are fundamental requirements to building testable, scalable systems in teams. Having global variables has been bad practice for what, 20 years? One of the first lines in Javascript, the Good Parts (closest book handy) is: JavaScript is built on some very good ideas and a few very bad ones. ... The bad ideas include a programming >model based on global variables.
- kasey_junk 9y agothis article falls into a particular genre "sensible advice that is either so internalized in other languages or the language itself prevents the pathology so why would you write that." It's a winning combo because you can recycle old and simple ideas for a whole new context. [edit] felt bad because rereading this it implies the author has bad intentions. Don't mean that. Just mean it's cute to watch the golang community learn Java.
- yumaikas 9y agoIn some ways, I consider Go to be a bit of a redo of the early ideas of Java, but with a bit more focus on tooling, and a stronger aim towards simplicity. And Go's standard library authors have the benefit of seeing how Java's early days played out. I do enjoy that Go gives a bit more obvious control over memory layout than Java did, and I enjoy using it as a small projects language. I've never used it in full anger/production, so I haven't seen the worst of it. One funny thing about Guice(mentioned elsewhere): I've heard more than one Googler say that dislike the framework quite a bit, due to how heavily it can be abused. One bit of advice that seems to have gone either missing in Java/JVM, or common knowledge, is the idea that only main/binary packages should have config options via flags/env vars. Many, many JVM libraries do not follow that convention, and I've had to hunt down a log4j XML file for Spark in order to turn down the logging levels before. Would much rather have that be controlled via a flag.
- chubot 9y ago
- bitwize 9y agoAn internet discovers dependency injection. Hackernews responds, "Well of course we knew THAT." The Rust Evangelism Strikeforce is off on a secret mission in the Balkans.
- hota_mazi 9y agoThat's not dependency injection, it's dependency passing.
- dbenhur 9y agoIt's the method of DI known as Constructor Injection. https://en.wikipedia.org/wiki/Dependency_injection#Constructor_injection https://en.wikipedia.org/wiki/Dependency_injection#Construct...
- MrBuddyCasino 9y ago"DI isn't idiomatic in Go" is what I've heard quite a few times. Java did it so it must be wrong.
- bitwize 9y agoWhich pretty much confirms that Go is engineered to do things the stupid way around, and that developers inculcated in its idioms are just now beginning to see the benefits of what boring old Java programmers have been doing for a decade or more.
- weberc2 9y agoI've only heard this in reference to DI frameworks. DI is very idiomatic.
- weitzj 9y agoI favor an DI style as well, but sometimes in rare cases I like using the init functions. This boils down to functions which follow the template.Must pattern, i.e.: either the function can be evaluated or the program should panic. The other case is read-only lookup tables.
- jimrandomh 9y agoI think the author's two examples - a logger and a database connection - should be handled differently from each other. The database connection should be passed as a parameter, and the logger should be a global variable. One difference is that it's reasonable to add logging to nearly any function anywhere, which means that you have to either make the logger global, preemptively provide a logger parameter to almost everything, or else change a whole bunch of method signatures at once every time you need to add logging to somewhere that didn't previously have any log statements. Global variables are unfortunate, but the other options are much worse. The second difference is that you pretty much only ever have one logger object in a process, but processes often use multiple database connections and it may matter which one is being used. For example, a caller may want to make your function's database accesses part of its transaction. Or use a unit test database instead of a real database. Or a number of other things. Omitting the parameter hides the fact that a function uses the database, which is an important thing to know about it.
- lsiebert 9y agoYou may have many different loggers, and different loggers for different parts of an app. For example, you don't want to log certain database or password related stuff explicitly as it creates a security issues. Also it's a lot easier to test logging, or anything that you would us a global object for, if it's DI based. That said, you can always pass a config object that holds what would normally be global variables but allows you to override.
- marcus_holmes 9y agoThe stdlib does include some globals/statics, for good reasons (the default logger, the default http mux). I'm always wary about advice that says "you must never use {this tool provided by a language}". Never say never ;) Globals can be and are useful in some circumstances. Having a blanket ban on globals because "globals are bad mmkay" is not useful. I draw this line at "do I need to mock this for testing?". If I need to mock it to run tests, then it shouldn't be a global and should be DI'd (so I can DI the mock). If I'm not going to mock it, then it's probably fine to leave it as a global. The Db and Logger are perfect examples: I frequently mock the Db connection, so I can test sql output. I almost never mock the logger, because I only use it to output meta information. So having a global logger is OK. YMMV...
- lpghatguy 9y agoShouldn't need a DB connection to create strings. Sometimes want to turn off, scope, or classify logging especially during testing. When these new levels of granularity are desired in your project, switching from globals is no fun.
- obstinate 9y agoI don't think, in the limit, that this is a scalable approach. While I think injecting dependencies is fine in some cases, large programs might have hundreds or thousands of collaborators. It is simply not feasible to instantiate all of them in the main function then pipe all of them down to the package that uses them. This is especially true when you keep the interface small like this program seems to. I think making the dependency explicit is good when it is likely that it is going to be desired to change the implementation on a per object basis. If it's unlikely or meaningless to have multiple different implementations within the same process, there's nothing wrong with making the configuration global. Imagine how painful it would be to use the http module if it required the injection of its clock, compression suite, network interfaces, address resolvers, loggers, etc. And that's just one module of many.
- tagrun 9y agoThe author is basically complaining that Go (and its standard library) isn't functional.