DEV Community

Discussion on: My reflections on Golang

Collapse
 
gypsydave5 profile image
David Wickes

Enjoyed that - a relatively balanced piece. Just a few corrections and observations

Dependency management

Was 'fixed' last year - making projects like Glide et al a bit obsolete. People are either moving to modules or are still using dep. Although that said it's not often that I need many dependencies as the standard library is very comprehensive.

In real-world projects, you always will end up creating complex structs, especially if the application is doing some generic json or yaml parsing and soon your code will start to look like this.

I looked at your ... um... I want to call it a struct, but I think "side pyramid of hell" would be a better term. If you're using this in a project - real world or any other world - then you've done something seriously wrong or you're trying to consume data the structure of which you have no prior knowledge about.

A huge smell is all the map[interface{}]map[interface{}]interface{} chaos. If you're using interface{} as a type all the time then you're basically writing a very verbose dynamically typed language with no compile time checking. Try and refactor to named types. Useful tools exist for parsing JSON and XML into Go types which can then be massaged by loving human hands (ie. gojson.com).

The interface concept in Go is weird.

But it's the same as in TypeScript - structural sub-typing (on methods in Go). Could you explain what you dislike about Go interfaces as opposed to TypeScript?

and you can accidentally implement someone's interface by just naming your method a certain way.

Sure - but has anyone ever done this, ever implemented the io.Writer interface accidentally and then passed this offending type to a function that's expecting it?

Go being too simple means you would have to write a lot of code as the language doesn't offer constructs like map, reduce, and so on, and add the lack of generic on top means you would end up writing a lot of utility code and a lot of that will be repeated to accommodate different types. Imagine writing a map function in Go, you would have to write one for every combination of Map that can be used. These factors don't make it easy to do DRY programming in Go.

Sure, you can't implement map or reduce in Go. There are no generics. Fine. When was the last time you needed to implement map or reduce? When was the last time you needed a function to be polymorphic over more than three types? In reality, it rarely happens. You don't map over a generically iterable type, you just write a for loop and get on with your life. I've seen more good code spoiled by an attempt to dry it out through a bad abstraction than I have seen it improved by generification.

Collapse
 
deepu105 profile image
Deepu K Sasidharan

Enjoyed that - a relatively balanced piece. Just a few corrections and observations

Thank you. I agree with some of your points and differ with some, also some of my concerns were more personal nitpicks and might not apply to everyone. But I think its good that we have constructive criticism. As I said in the article I do like Go and think it has a purpose and It does some things really well, I appreciate that but the takeaway from the article should be "Using the right tools for the right Job"

Dependency management

Was 'fixed' last year - making projects like Glide et al a bit obsolete. People are either moving to modules or are still using dep. Although that said it's not often that I need many dependencies as the standard library is very comprehensive.

I'm aware of modules and dep. Dep is still experimental and modules do look promising, but if you look at many popular Go projects in GitHub, seems like they are still using Glide or another system and not modules(of course it's their bad). But my point was that dependency management in Go is immature and I still stand by that. For example, if you have used NPM, Yarn, Maven or Gradle, its far better and mature than Go modules even with its shortcomings. Also in my project, we are using Gradle with Gogradle and it works fine but the experience is not as enjoyable as others as I mentioned. Also, I still don't see anything like npm link in Go modules. You have no idea how valuable it is unless you have developed some mode projects with dependencies, I'm seriously missing those here. Let me know if there is something similar in the Go world.

Also, I don't agree with "it's not often that I need many dependencies" as a generic statement. May be projects you do are simple enough to avoid that or you are reinventing the wheel all the time using language features. In most real-world cases you would need many dependencies unless you are into reinventing the wheel for everything.

In real-world projects, you always will end up creating complex structs, especially if the application is doing some generic json or yaml parsing and soon your code will start to look like this.

I looked at your ... um... I want to call it a struct, but I think "side pyramid of hell" would be a better term. If you're using this in a project - real world or any other world - then you've done something seriously wrong or you're trying to consume data the structure of which you have no prior knowledge about.

Maybe you misunderstood me. No one in their right mind should design structs like these, it was an example to show what is possible though. My pain point was when using generic parsing libs which indeed produce structures which look worse then these and hence is pain during debugging, mocking and test case creation. Also, it was a nitpick. And when you do generic data processing sometimes you don't know the structure or sometimes the structure has to be kept generic, for example, a yaml where a field can take values of different types. Many times when you work on real-world projects you have no control of what data structure will come in.

A huge smell is all the map[interface{}]map[interface{}]interface{} chaos. If you're using interface{} as a type all the time then you're basically writing a very verbose dynamically typed language with no compile time checking. Try and refactor to named types. Useful tools exist for parsing JSON and XML into Go types which can then be massaged by loving human hands (ie. gojson.com).

Agree, no one should ever write structs with interface{} as much as possible, but sometimes it's inevitable. As I said an example is when you have Yaml or Json doc that can take values of different types. We had a use case where our Yaml fields needed to take string, numeric, boolean values as well as complex Yaml expressions or functions. the only way to parse them is to declare those as interfaces and then do a type switch in code to process them.

The interface concept in Go is weird.

But it's the same as in TypeScript - structural sub-typing (on methods in Go). Could you explain what you dislike about Go interfaces as opposed to TypeScript?

No, it is not the same. In Typescript, interfaces are implemented by intent as below unlike the implicit(which is what I don't like about it) way in Go and also interfaces in TS also represent type a bit similar to a struct IMO.

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}

and you can accidentally implement someone's interface by just naming your method a certain way.

Sure - but has anyone ever done this, ever implemented the io.Writer interface accidentally and then passed this offending type to a function that's expecting it?

I don't think we can answer that unless people who do that come out and say so right? I at least always have to think twice before naming a method(Which might be a good thing in some cases :P). Anyway, my point was that it is possible and also its a nitpick.

Go being too simple means you would have to write a lot of code as the language doesn't offer constructs like map, reduce, and so on, and add the lack of generic on top means you would end up writing a lot of utility code and a lot of that will be repeated to accommodate different types. Imagine writing a map function in Go, you would have to write one for every combination of Map that can be used. These factors don't make it easy to do DRY programming in Go.

Sure, you can't implement map or reduce in Go. There are no generics. Fine. When was the last time you needed to implement map or reduce? When was the last time you needed a function to be polymorphic over more than three types? In reality, it rarely happens. You don't map over a generically iterable type, you just write a for loop and get on with your life. I've seen more good code spoiled by an attempt to dry it out through a bad abstraction than I have seen it improved by generification.

Of course, everything can be done using the simple features Go provides and that is exactly my point, you have to write a lot of code for everything which is anything but DRY(I also believe in responsible DRY rather than doing it for the sake of doing it) and feels very verbose and annoying.

If you are used to doing map/reduce etc in other languages it just feels verbose doing the same all the time, again and again, using for loops. It's not an enjoyable experience at least for me. I can provide many examples of when you need map/reduce etc and when you need polymorphism but my intention is not to prove that Go is bad these are just reflections of someone who would like to see Go get better and better.

Anyways thanks for the comment, I really enjoy such healthy discussions and debate.

Also what Ben pointed out below also matters.

Collapse
 
gypsydave5 profile image
David Wickes

No, it is not the same. In Typescript, interfaces are implemented by intent as below unlike the implicit(which is what I don't like about it) way in Go

I must object. Although you can use interfaces to enforce contracts, they can also be used as structural sub-typing on fields - see this from the Typescript docs:

interface LabeledValue {
    label: string;
}

function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

Notice we didn’t have to explicitly say that the object we pass to printLabel implements this interface like we might have to in other languages. Here, it’s only the shape that matters. If the object we pass to the function meets the requirements listed, then it’s allowed.

Thread Thread
 
deepu105 profile image
Deepu K Sasidharan

Oh Yes, you are right, I never thought of Duck typing coz I used to avoid it to the best of my abilities. Interestingly, it is considered as an issue by many and there is an open ticket in TS project to change this behavior as it can cause unexpected bugs.

My problems with Go is in enforcing contracts and not in typing but regardless I understand what you mean and to be fair the exact same implicit behavior can be achieved in Typescript as well which I hated.

class Vehicle {
    run(): void { console.log('Vehicle.run'); }
}

class Task {
    run(): void { console.log('Task.run'); }
}

function runTask(t: {  run(): void }) {
    t.run();
}

runTask(new Task());
runTask(new Vehicle());

But a difference, however, is that in TS its is not the only way and I have the choice to do things explicitly without relying on the implicit behavior. And I have always used the explicit way in TS projects as far as I could remember. And seems like there are ways to workaround but they are quite verbose

Anyways, unlike Go, TS is just a superset of JS which is completely dynamic(so in the end, all the types and interfaces, etc are only syntax sugars) and due to the weak typed nature of JS allowing structural typing is the only way to emulate how JS works so its more of a limitation than a feature IMO.

Collapse
 
thebuzzsaw profile image
Kelly Brown

When was the last time you needed a function to be polymorphic over more than three types?

Today. Yesterday. The day before. Can we have generics now?

Collapse
 
gypsydave5 profile image
David Wickes

Only if you ask Father Christmas nicely.

Or rsc.

Collapse
 
hasnatbabur profile image
Hasnat Babur • Edited

If you are planning next Google then Golang is good for you but for most people Java and it's huge ecosystem is more than enough. Also Java community is not just eating peanuts. They are evolving Java a lot that will take Golang next 10 years at least to achieve.