Closed
Description
User story: I'm currently writing a REST API in Go, which uses the encoding/json package. The Update part of CRUD gets tricky if you want to do partial updates because there is no way to tell if something was left out of the JSON. The structure, for example, could look like this: type Message struct { Name string Title string Message string Date time.Time } With an update I may only want to update Title. To do this I have to change the structure to this: type Message struct { Name *string Title *string Message *string Date *time.Time } This allows each value to have 3 states: nil, empty "", or non-empty. Unfortunately this approach isn't very desirable because the values themselves don't live in the struct, and this can create garbage that needs to be collected. With structs that have an optional time.Time, changing it to *time.Time doesn't solve the problem either. You can't tell the difference between {"Date":null} and {} (remove expiration date vs don't touch it). A possible solution to this is by wrapping the types like this: type Int struct { int set bool } And this works very well. In fact, database/sql does this. Full example of Int: http://play.golang.org/p/GbsOucDAPZ Example of Time: http://play.golang.org/p/OpVVOVBIwc Unfortunately there is no standardization for wrappers like this. I can use sql.NullInt64 and keep my data store, model, and controller layers separate. Since my data store needs to be interchangeable, what if I'm not using SQL? A standard package to hold these wrapper types would be desirable. And they could include methods like MarshalJSON and UnmarshalJSON. But what happens when a custom package needs methods such as these? Solutions: Option 1 - A standard package for wrapper types could be created. Either there needs to be a way to attach methods to a type declared in one package from another, or the encoding packages need to be aware of these types just as they are the primitives. Pros -- If the packages are aware of these types (and don't use Marshaler/Unmarshaler methods) only an addition pkg written in Go needs to be added. Cons -- This only works well for primitive types. Custom types such a time.Time rely on Marshaler/Unmarshaler methods. These method are difficult to add for custom encoders / database packages, etc.. (packages that use reflect). Option 2 - A type extension, which could be expressed something like: type Message struct { Name ?string Title ?string Message ?string Date ?time.Time } Internally it would be the same as the wrappers and it would work with a built-int isset() method. Otherwise the interaction would be identical to the regular types, so you could do something like msg.Name = "Luke" instead of something like msg.Name.Set("Luke"). This would also be true with the reflect package, with the addition of Value.IsSet. This should be kept distinct from pointers. unset(msg.Name) instead of msg.Name = nil. An unset() method would set a value to it's zero value and reset the internal bool. Pros -- Use these types like normal types. Marshal/Unmarshal method would be inherited from the types themselves. Cons -- The spec needs to change to allow support for this. I cannot speak for the challenges in doing this.