Hacker News new | ask | show | jobs
by docker_up 2802 days ago
My biggest problem with Go is the lack of a constructor. I don't feel that a struct type is good enough to enforce data integrity and as you have a more complex program, you need to know that the object you are being passed has data integrity.

With a constructor, I can force data to conform to what I need it to. I can ensure that certain fields are not nil, that they conform to a specific list of values, etc.

Because of a lack of a constructor in Go, I can't do that and I need to continuously validate the data, which is annoying and a source of bugs. It can be said that this can be accomplished with interfaces but that's adding a lot of complexity to something that should be a lot easier to handle, in my opinion.

5 comments

By authoring private structs with public constructors, you can get what you want out of this. Gets a bit messy with testing, but not impossible.
If you have your tests within the same package, they can access the private members of structs directly, which helps avoid some of the mess.

If you’re in a different package things aren’t too bad if you have meaningfully abstracted interfaces. And with Go’s duck typing, even if you’re using another package that doesn’t have good abstraction, you can create your own interfaces with the functionality you need for mocking purposes.

You're getting downvoted here, but I agree with you. Once you really lean on Java constructors to guarantee initialized state, it's hard to go to something like Go that doesn't have this. Sure, you could do a lot of gymnastics to try to work around it, but that's the thing about Java that's nice. It's just built-in with that in mind.
Having written a lot of C++, constructors are a bad idea. Two main reasons:

1. Error handling is difficult - they don't return anything, so you are forced to use exceptions, and throwing exceptions in constructors is awkward. Go doesn't even have exceptions so it can't do that.

2. They don't have names. Often this is fine - you only have one way to construct an object. But if you have more than one, then you have two unnamed functions that do different things. And if they have the same parameters you end up with crazy workarounds like adding dummy parameters.

The way Rust does it is far superior. Basically you have a static function that creates the object. It's a normal function, so it can return errors (or the object), and you can name it, so you can have `Circle::new_with_radius(float r)` and `Circle::new_with_diameter(float d)` with no confusion.

Much better.

How about just using the builder pattern?
Not familiar with go or that pattern but is this a good example? https://gist.github.com/vaskoz/10073335
Yes, that's a good example
have an initSomething() that returns an initialized struct.
How is this any different than namespace_init(); functions from C?

I hear language complaints like this all the time, and it always sounds like developers grasping for something to complain about.

I'm not even a Go fan, but I think you can cut the language some slack here at least in this department.

The entire approach Go is selling from what I've hear is they're entirely forgoing the traditional object-oriented (in the C++ and Java sense of the term) approach for composability. Okay, sure, whatever, good for Go developers; but you can't complain about a leopard having spots. Just accept it or move on.

> How is this any different than namespace_init(); functions from C?

It's not; presumably that's the point. C isn't exactly famous for its support for data hiding and enforcing invariants.