top | item 44335829

(no title)

e_y_ | 8 months ago

As someone who isn't a Go programmer, on the face of it using strings (struct tags) for field metadata seems pretty backwards compared to Rust macros (which parses the metadata at compile time) or Java annotations (which are processed at runtime but at least don't require parsing a string to break apart options).

The accidental omitempty and - are a good example of the weirdness even if they might not cause problems in practice.

discuss

order

xnorswap|8 months ago

As a .net programmer, the "stringly typed" nature of the metadata horrifies me, but the choices of Go have long confused me.

So in .NET, like Java as you mention, we have attributes, .

e.g.

    [JsonPropertyName("username")]
    [JsonIgnore]
etc.

This is simple, and obvious. The JsonPropertyName attribute is an override, you can set naming policies for the whole class. camelCase by default, with kebab-case, snake_case etc as alternative defaults.

C#/.NET of course has the benefit of having public properties, which are serialised by default, and private properties, which aren't, so you're unlikely to be exposing things you don't want to expose.

This contrasts to Go's approach, much like python, of using casing convention to determine private vs public fields. ( Please correct me if I'm wrong on this? )

The first example still confuses me though, because either you want IsAdmin to come from the user, in which case you still want to deserialise it, or you don't, in which case it shouldn't even be in your DTO at all.

Deserialisation there is a bit of a red-herring, as there should be a validation step which includes, "Does this user have the rights to create an admin?".

The idea of having a user class, which gets directly updated using properties straight from deserialized user input, feels weird to me, but I'd probably be dismissed as an "enterprise programmer" who wants to put layers between everything.

hmry|8 months ago

> This contrasts to Go's approach, much like python, of using casing convention to determine private vs public fields. ( Please correct me if I'm wrong on this? )

I think calling it a convention is misleading.

In Python, you can access an _field just by writing obj._field. It's not enforced, only a note to the user that they shouldn't do that.

But in Go, obj.field is a compiler error. Fields that start with a lowercase letter really are private, and this is enforced.

So I think it's better to think of it as true private fields, just with a... unique syntax.

masklinn|8 months ago

> This contrasts to Go's approach, much like python, of using casing convention to determine private vs public fields. ( Please correct me if I'm wrong on this? )

Go actually ties visibility to casing, instead of using separate annotations. And it will not serialise private fields, only public.

Python has no concept of visibility at all, conventionally you should not access attributes prefixed with `_` but it won't stop you.

grey-area|8 months ago

Not just weird, it’s dangerous to do this - to easy to miss validation as fields are added over time. Better to explicitly validate all fields IMO.

liampulles|8 months ago

As someone who is regular Go programmer, yes struct tags do suck. It gets even worse if you want to try and combine multiple "annotations" into one struct tag string.

The reason its like that is that Go philosophically is very much against the idea of annotations and macros, and very strongly about the idea of a clear upfront control flow, and this is one of the reasons I love the language. But it does come at the cost of a few highly useful usecases for annotations (like mapping JSON and XML, etc.) becoming obtuse to use.

The idea of more compile-time macros in Go is interesting to me, but at the same time the ease of debugging and understanding the Go control flow in my programs is one of the reasons I love it so much, and I would not want to invite the possibility of "magic" web frameworks that would inevitably result from more metaprogramming ability in Go. So I guess I'm prepared to live with this consequence. :/

masklinn|8 months ago

> The reason its like that is that Go philosophically is very much against the idea of annotations and macros, and very strongly about the idea of a clear upfront control flow

Annotations have no control flow, they just attach metadata to items. The difference with struct tags being that that metadata is structured.

valenterry|8 months ago

I understand your feeling. There are many magical frameworks like e.g. Spring that do these things and it's super hard to figure out what's going on.

The solution is usually to have an even better language. One, where the typesystem is so powerful, that such hacks are not necessary. Unfortunately, that also means you have to learn that typesystem to be productive in language, and you have to learn it more or less upfront - which is not something that Google wanted for golang due to the turnover.

theasisa|8 months ago

I find the most annoying thing to be that there is no standard for separate values or field in the tag string. Sometimes you use a comma (json) sometimes a semicolon (Gorm IIRC) etc. This has caused me several headaches.

kjksf|8 months ago

For some it's stupidity. For others it's brilliance.

It's one of many examples of 80/20 design in Go: 80% of functionality with 20% of complexity and cost.

Struct tags address an important scenario in an easy to use way.

But they don't try to address other scenarios, like annotations do. They are not function tags. They're not variable tags. They are not general purpose annotations. They are annotations for struct fields and struct fields only.

Are they are as powerful as annotations or macros? Of course not, not even close.

Are they as complex to implement, understand, use? Also not.

80/20 design. 80% of functionality at 20% of cost.

Philpax|8 months ago

Go's simplifications often introduce complexities elsewhere, however, as this article demonstrates with the complexities of correctness of a stringly-typed DSL.

There's no free lunch here, and the compromises Go makes to achieve its outcomes have shown themselves to be error-prone in ways that were entirely predictable at design time.

timeon|8 months ago

Starting to get feeling that 80/20 design is not good thing. Many things seems to be driven by worse is better but, looking at things like Climate Change... was it worth it?

grey-area|8 months ago

Yes they are a horrible idea for many reasons, not just security. It’s like a hidden ill-defined poorly understood dsl in strings.

You can just not use them though - you can unmarshal to a map instead and select the keys you want, perform validation etc and then set the values.

Same when publishing - I prefer to have an explicit view which defines the keys exposed rather than than publishing all by default based on these poorly understood string keys attached to types.

reactordev|8 months ago

struct tags greatly reduce the boilerplate code required to map fields to fields. It’s really quite novel once you understand it.

masklinn|8 months ago

> struct tags greatly reduce the boilerplate code required to map fields to fields.

Are you somehow under the impression that Go is unique in having a terse way to map fields to fields?

> It’s really quite novel once you understand it.

It's the opposite of novel, putting ad-hoc annotations in unstructured contexts is what people used to do before java 5.

jlouis|8 months ago

It's not very novel. There's far better ways of solving this than allowing a random string to be embedded as aux information to a struct field. Examples: F# type providers, or OCamls PPX system for extending the language in a well defined way. Macro rewriting systems also allow for better safety in this area.

This allows you to derive a safe parser from the structural data, and you can make said parser be really strict. See e.g., Wuffs or Langsec for examples of approaches here.