Go is absurd. It's opinionated in all the wrong ways. > Functions that return something are given noun-like names. > // Good: > func (c Config) JobName(key string) (value string, ok bool) > A corollary of this is that function and method names should avoid the prefix Get. > // Bad: > func (c Config) GetJobName(key string) (value string, ok bool) That's dumb. I'd like a function to be GetJobName to indicate that it do…
Just assume that "Noun()" gets the noun and it's effectively the same as "GetNoun()". Of course, nothing in the language prevents you from using "GetNoun()" if you strongly prefer that. > The other day, I spent a whole day trying to figure out the "idiomatic" way to return a an object not found case from my db. Do you return a nil pointer (don't, passing pointers leads to bugs), or an empty struct (then how do you re…
Go Style
191–200 of 212 posts
Re: Go Style
#192>Concise Go code has a high signal-to-noise ratio. A few lines later: // Good: if err := doSomething(); err != nil { // ... } "Tell me, how many lights you see?"
What's wrong with this? The code is doing... exactly what it says. Call doSomething and if it returns an error do something else. What part of that is noise?
Re: Go Style
#193Earlier quoted context omitted.
Just assume that "Noun()" gets the noun and it's effectively the same as "GetNoun()". Of course, nothing in the language prevents you from using "GetNoun()" if you strongly prefer that. > The other day, I spent a whole day trying to figure out the "idiomatic" way to return a an object not found case from my db. Do you return a nil pointer (don't, passing pointers leads to bugs), or an empty struct (then how do you re…
One more cognitive jump to get to the point. Programming languages are for people, so reducing any mapping between what is written and what is meant is for the better. Adding more jumps, however easy, is rarely the right thing.
Re: Go Style
#194Earlier quoted context omitted.
Interfaces are the most misused feature of Go. I think people like to prototype their API by typing in all the functions they intend to implement, so the interface acts as a template for their program that they can fill out. This, unfortunately, is not what they're for. I have a longer rant about this here: https://jrock.us/posts/go-interfaces/
Wow, I find this quite interesting, however, there are probably some real downsides to this? Maybe someone else can highlight some of those. An issue I can think of is for instance: consider a system where each time a new request comes in a new transaction is started. That situation would result in having a new `DBImpl struct` for each request. It seems that that last "but you probably only have one of these DB objec…
Re: Go Style
#195Earlier quoted context omitted.
Interfaces are the most misused feature of Go. I think people like to prototype their API by typing in all the functions they intend to implement, so the interface acts as a template for their program that they can fill out. This, unfortunately, is not what they're for. I have a longer rant about this here: https://jrock.us/posts/go-interfaces/
Wow, I find this quite interesting, however, there are probably some real downsides to this? Maybe someone else can highlight some of those. An issue I can think of is for instance: consider a system where each time a new request comes in a new transaction is started. That situation would result in having a new `DBImpl struct` for each request. It seems that that last "but you probably only have one of these DB objec…
Looking at database code I have written, I never do the object thing (but was replying to an author who does). I typically have a package with functions that take transactions as an argument, like this:
https://github.com/jrockway/jsso2/blob/master/pkg/store/sess...
(If you poke around the package, methods that don't make more than one database call take a sqlx.ExtContext instead of a *sqlx.Tx. Functions that need to start their own transactions take the connection instead, though I'm not sure I'd recommend that approach because it hurts composability.)
To test it, I just run against an actual Postgres instance which is pretty easy to find. (I am not sure I would steal my database setup / teardown utilities from that project. The tests end up indented too far. A more idiomatic Go way is "app := testapp.New(); defer app.Cleanup(); tests go here"; in that codebase I picked the "testapp.Test(t, func(t testing.TB) { tests go here })" pattern. Definitely something I look for and avoid these days, but didn't in 2020 ;)
Re: Go Style
#196Earlier quoted context omitted.
Interfaces are the most misused feature of Go. I think people like to prototype their API by typing in all the functions they intend to implement, so the interface acts as a template for their program that they can fill out. This, unfortunately, is not what they're for. I have a longer rant about this here: https://jrock.us/posts/go-interfaces/
Thank you for sharing this. I was struggling with this just today: Realizing I keep putting off writing tests for some modules because mocking a certain mega interface is a pain. Your post gives me some great hints for things to try. Looking forward to experimenting with it.
Adding private members to my structs for testing was something the Go team forced me kicking and screaming to do when I worked at Google. I remember writing this adaptor for some internal network filesystem, and had an interface and two implementations: one that used the network for real, one that did everything in-memory for tests. I sent it off for code review and they were like "nope! don't do that in go!" and suggested an if statement in every method to handle the in-memory implementation. I was unhappy about it for months because it went against everything I knew about programming at the time, but like 10 years later, I appreciate that they were right. (I will say that everyone I explain this to has approximately the reaction that I had at the time. It is an easier pill to swallow when the person telling you not to do it wrote Go, but they don't have that benefit ;)
Re: Go Style
#197Earlier quoted context omitted.
Wow, I find this quite interesting, however, there are probably some real downsides to this? Maybe someone else can highlight some of those. An issue I can think of is for instance: consider a system where each time a new request comes in a new transaction is started. That situation would result in having a new `DBImpl struct` for each request. It seems that that last "but you probably only have one of these DB objec…
Sure, if you want to compose the various methods into a single transaction, then each business-logic-implementing function needs to take a transaction object as a parameter. In that case, you don't even need something like DBImpl. Looking at database code I have written, I never do the object thing (but was replying to an author who does). I typically have a package with functions that take transactions as an argumen…
Re: Go Style
#198Earlier quoted context omitted.
Thank you for sharing this. I was struggling with this just today: Realizing I keep putting off writing tests for some modules because mocking a certain mega interface is a pain. Your post gives me some great hints for things to try. Looking forward to experimenting with it.
It took me a long time to figure out how to make small interfaces. I had an "aha!" moment not too long before writing that post. It seems bad, then it suddenly makes sense, and then the other way seems bad. Adding private members to my structs for testing was something the Go team forced me kicking and screaming to do when I worked at Google. I remember writing this adaptor for some internal network filesystem, and h…
Re: Go Style
#199Earlier quoted context omitted.
I agree that most types come with a natural variable name. However, in many cases a more descriptive name is way appropriate. On top of my mind: - Multiple variables of the same type. How are you supposed to distinguish between req and req2? Compare it to something like "apiReq" and "cdnReq" - Primitive types, that does not inherently carry a domain value. An integer called "seconds" or "max_offset" has a lot more me…
You’re right. Going back to my physics example, all the variables in a physics problem are actually of the same type, float. So you’d need more specific types, but that would be infeasible-the whole point of types in a general purpose language is that they’re general enough for any use case. If you were just coding physics problems you could have types restricted to the physics domain, that is, physical units. Which…
In Go, I would probably model this as:
type mass float64
type speed float64
type acceleration float64
That way, they'd all have float64 as the "storage type", but it would be harder to accidentally pass an acceleration when I wanted a mass.Re: Go Style
#200Earlier quoted context omitted.
When I started coding I'd type 10 FOR F = 1 TO 10 20 PRINT F, F*F 30 NEXT F Because "FOR" and "F" shared the same key, on my ZX Spectrum keyboard.
And for those that don't know - entering Basic code on the Spectrum (well the original one at least) was ALL single/shifted key shortcuts for keywords rather than typing them out. eg https://www.old-computers.com/museum/photos/sinclair_zx-spec... The low cost and quirky masochism was mainly why we loved it.