Remix.run Logo
nordsieck 5 hours ago

> In Go, it's possible to actually make a Goose and have it as just a local variable living on your stack, but, the language insists that anybody can conjure one into existence despite having no basis for doing so, the resulting Goose is at best now in some invalid "null" state.

If you really don't want people instantiating instances of a type on their own, can't you just make the type private but make a factory for the type public?

jerf 5 hours ago | parent | next [-]

"If you really don't want people instantiating instances of a type on their own, can't you just make the type private but make a factory for the type public?"

"Yes", but really, no. It is syntactically valid to write a function that returns an unexported type, and you can technically get an instance of that unexported type in your other package by using := to assign it to a variable. However, you can not name the type in the other packages at all. So, you can not say "var x mypkg.unexportedType", even if you can "x := mypkg.ReturnUnexportedType()". You can not put it in a struct/channel/slice/map/whatever, since you can't name it and there's no equivalent of := for types. You can't refactor the function that contains the "x" in any way that involves passing it to a new function because the type signature for the new function would have to mention the unexported type, which it can't do, and there is no inference for function types that would let you elide it. Reflect restrictions on unexported types are still in effect so even trying to put it in an "any" isn't really all that useful.

Basically, it's useless, even though it's just barely sort of valid, because it's much more than just creation that is blocked. In practice you have to export types you want users to be able to assign to, put in structs, etc., and if they are exported, their zero values can be created by any package that imports them. One of the standard linters in golangci-lint will warn you if you accidentally write an unexported type into an exported type or value, because you didn't mean that, even if you thought you did.

You can write an interface that you export, and then return an unexported data type that conforms to that interface. However, you then can't prevent someone from having a nil instance of that interface, so in terms of preventing invalid data, this doesn't really do anything useful. This is more useful for manipulating package documentation by not exporting types unnecessarily and having to write docs for them (or, if you don't write docs, having them appear in the godoc) then as access control.

stouset 5 hours ago | parent | prev | next [-]

Dangerous by default is still a pretty bad design choice.

evantbyrne 5 hours ago | parent [-]

There is no default. Functions, types, methods, and fields are either public or private based on capitalization.

actionfromafar 5 hours ago | parent | prev [-]

But the point is Go still allows bad patterns. You can always avoid bad patterns, but not everyone will. See C++20.

Perfectly possible to write beautiful code. Alas, hell is other coders, or something.

Edit hot take: I wish Go would just have been Pascal/Delphi rebranded. All language discussions just ignore we had a contender against C for decades.

If you say it’s too weird, don’t come dragging in Go in the same breath, thanks.