Documentation
¶
Overview ¶
Package xyz provides switch types, tuples and a binary sequence tag.
Tuples ¶
Tuples enable the repesentation of small sequences of values, where each value can have a different type. Tuples are marshaled as JSON arrays and include a method for extracting the values.
pair := xyz.NewPair(1, 2) x, y := pair.Split() trio := xyz.NewTrio(1, "hello", "world") x, y, z := trio.Split() quad := xyz.NewQuad(1, 2.0, 3, "4") a, b, c, d := quad.Split()
Tuples can be useful in situations where a single value is expected but where it would be more natural to use multiple values instead. For example, they can be used to capture function arguments, or to pack multiple types into a single generic type parameter.
Switch Types (Enums) ¶
Switch types can be used to switch on a set of known values.
To represent an enumerated type (enum) where each value must be distinct you can add fields to the switch type with the same type as the switch itself.
type Animal xyz.Switch[int, struct { Cat Animal Dog Animal }]
Note that neither switch types do not restrict the underlying value in memory to the set of values defined in the switch type, so a default case should be included for any switch statements on the value of a switch type.
Tagged Types (Unions) ¶
Tagged types are used to represent a discriminated set of values.
Each tagged case can have a variable value.
type MyValue xyz.Tagged[any, struct { String xyz.Case[MyValue, string] Number xyz.Case[MyValue, float64] }]
If you use a custom interface type as the underlying type to switch on, you can define a helper type to help ensure each case is asserted to implement that interface.
type is[T io.Reader] xyz.Case[MyValue, T] type MyValue xyz.Switch[io.Reader, struct { Bufio is[*bufio.Reader] Bytes is[*bytes.Reader] }]
In order to create a new tagged value, or to assess the value of a switch, you must create an accessor for the switch type. This is done by calling the Values method on the switch type. Typically this should be performed once and stored in a variable, rather than called on demand.
// the convention is to use either a plural form, or to add a New or For prefix to // the type name or a Values suffix. var ( NewAnimal = xyz.AccessorFor(Animal.Values) Animals = xyz.AccessorFor(Animal.Values) AnimalValues = xyz.AccessorFor(Animal.Values) )
The accessor provides methods for creating new values, and for assessing the class of value.
var hello = NewHelloWorld.Hello.As("hello") var value = MyValues.Number.As(22) var animal = Animals.Cat switch xyz.ValueOf(hello) { case NewHelloWorld.Hello: fmt.Println(NewHelloWorld.Raw()) case NewHelloWorld.World: fmt.Println(NewHelloWorld.Raw()) default: } // union values can be accesed using the Get method. switch xyz.ValueOf(value) { case MyValues.String: fmt.Println(MyValues.String.Get(value)) case MyValues.Number: fmt.Println(MyValues.Number.Get(value)) default: } // enum fields within a switch can be switched on directly. switch animal { case Animals.Cat: case Animals.Dog: default: }
Tagged values have builtin support for JSON marshaling and unmarshaling. The behaviour of this can be controlled with json tags. [Enum]-backed values are always marshaled as strings, switches with variable Case values need to be tagged in order to enable the values to be unmarshaled.
type Object struct { Field string `json:"field"` } // Each case may be matched by JSON type, the first type // match that unmarshals without an error, will win. type MyValue xyz.Switch[any, struct { Null MyValue `json:",null"` String xyz.Case[MyValue, string] `json:",string"` Number xyz.Case[MyValue, float64] `json:",number"` Object xyz.Case[MyValue, Object] `json:",object"` Array xyz.Case[MyValue, []int] `json:",array"` }] MyValue.String.As("hello").MarshalJSON() // "hello" MyValue.Number.As(22).MarshalJSON() // 22 // An implicit object can be used with different field names // for each case. type MyValue xyz.Switch[any, struct { String xyz.Case[MyValue, string] `json:"string"` Number xyz.Case[MyValue, float64] `json:"number"` }] MyValue.String.As("hello").MarshalJSON() // {"string": "hello"} MyValue.Number.As(22).MarshalJSON() // {"number": 22} // A discrimator field can be specified. type MyValue xyz.Switch[any, struct { String xyz.Case[MyValue, string] `json:"value?type=string"` Number xyz.Case[MyValue, float64] `json:"value?type=number"` Struct xyz.Case[MyValue, Object] `json:"?type=struct"` }] MyValue.String.As("hello").MarshalJSON() // {"value": "hello", "type": "string"} MyValue.Number.As(22).MarshalJSON() // {"value": 22, "type": "number"} MyValue.Struct.As(Object{"1234"}).MarshalJSON() // {"type": "struct", "field": "1234"}
The underlying memory representation can be designed to create switch values that more efficiently make use of memory. Each value element within a case, will be packed in to the available bytes of the switch type. Pointer-like-values are packed into an available pointer field, of either a correctly typed pointer, a compatible interface, or else an unsafe.Pointer. Fixed fields will be consumed first, followed by slices or maps which will grow as needed. If the value cannot be stored in the available memory, then a pointer will be used to store the value.
For example, the following switch type can store up to 8 bytes of data directly, with an additional pointer field to catch larger values (such as strings).
type container struct { raw [8]byte ptr unsafe.Pointer } type Any xyz.Switch[container, struct { B xyz.Case[Any, bool] I xyz.Case[Any, int] S xyz.Case[Any, string] }]
Index ¶
- func AccessorFor[S any, T any, V func(S, internal) T](values V) T
- func Raw[T switchWith[Storage, Values], Storage any, Values any](val Storage) T
- type Case
- type CaseReflection
- type Duplex
- type Extend
- func (v Extend) Get() (Storage, bool)
- func (v Extend) Interface() any
- func (v Extend) MarshalJSON() ([]byte, error)
- func (v Extend) MarshalPair() (Pair[string, Storage], error)
- func (v Extend) MarshalText() ([]byte, error)
- func (v Extend) Reflection() []CaseReflection
- func (v Extend) String() string
- func (v Extend) Tag() reflect.StructTag
- func (v *Extend) UnmarshalJSON(data []byte) error
- func (v *Extend) UnmarshalPair(pair Pair[string, Storage]) error
- func (v *Extend) UnmarshalText(data []byte) error
- func (Extend) Validate(val any) bool
- func (v Extend) Values(internal) Values
- type Extern
- func (v Extern) Get() (Storage, bool)
- func (v Extern) Interface() any
- func (v Extern) MarshalJSON() ([]byte, error)
- func (v Extern) MarshalPair() (Pair[string, Storage], error)
- func (v Extern) MarshalText() ([]byte, error)
- func (v Extern) Reflection() []CaseReflection
- func (v Extern) String() string
- func (v Extern) Tag() reflect.StructTag
- func (v *Extern) UnmarshalJSON(data []byte) error
- func (v *Extern) UnmarshalPair(pair Pair[string, Storage]) error
- func (v *Extern) UnmarshalText(data []byte) error
- func (Extern) Validate(val any) bool
- func (v Extern) Values(internal) Values
- type Maybe
- type Pair
- type Quad
- type Static
- type Switch
- func (v *Switch) GobDecode(data []byte) error
- func (v Switch) GobEncode() ([]byte, error)
- func (v Switch) Interface() any
- func (v *Switch) InterfaceAddr() any
- func (v Switch) MarshalText() ([]byte, error)
- func (v Switch) Raw() Storage
- func (v *Switch) RawPointer() *Storage
- func (v Switch) String() string
- func (v *Switch) UnmarshalJSON(data []byte) error
- func (v *Switch) UnmarshalText(data []byte) error
- func (v Switch) Values(internal) Values
- func (v Switch) ValuesJSON() (oneof []json.RawMessage)
- type Tagged
- func (v Tagged) Get() (Storage, bool)
- func (v Tagged) Interface() any
- func (v Tagged) MarshalJSON() ([]byte, error)
- func (v Tagged) MarshalPair() (Pair[string, Storage], error)
- func (v Tagged) MarshalText() ([]byte, error)
- func (v Tagged) Reflection() []CaseReflection
- func (v Tagged) String() string
- func (v Tagged) Tag() reflect.StructTag
- func (v *Tagged) UnmarshalJSON(data []byte) error
- func (v *Tagged) UnmarshalPair(pair Pair[string, Storage]) error
- func (v *Tagged) UnmarshalText(data []byte) error
- func (Tagged) Validate(val any) bool
- func (v Tagged) Values(internal) Values
- type Trio
- type TypeOf
- type Validator
- type When
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AccessorFor ¶
AccessorFor retu an accessor for the given switch type. Call this using the typename.Values, ie. if the switch type is named MyType, pass MyType.Values to this function.
Types ¶
type Case ¶
type Case[Variant isVariant, Constraint any] struct { // contains filtered or unexported fields }
Case indicates that a value within a variant can vary in value, constrained by a particular type.
func (Case) As ¶
func (v Case) As(val Constraint) Variant
As returns the value of the variant as the given type.
func (Case) Get ¶
func (v Case) Get(variant Variant) Constraint
Get returns the value of the variant as the given type.
type CaseReflection ¶
type CaseReflection struct { Name string Tags reflect.StructTag Vary reflect.Type Test func(any) bool }
CaseReflection is equivalent to a reflect.StructField for switch types.
type Duplex ¶
type Duplex[T addable] []T
Duplex numbers (a±b±c...)
type Extend ¶
type Extend[Extern isVariantWith[Storage], Storage any, Values any] struct { // contains filtered or unexported fields }
Extend an Extern type with the given switch values.
func (Extend) MarshalJSON ¶
func (Extend) MarshalPair ¶
MarshalPair based on the json field/type name and the storage value.
func (Extend) MarshalText ¶
func (Extend) Reflection ¶
func (v Extend) Reflection() []CaseReflection
func (*Extend) UnmarshalJSON ¶
func (*Extend) UnmarshalPair ¶
UnmarshalPair based on the json field/type name and the storage value.
func (*Extend) UnmarshalText ¶
type Extern ¶
Extern is a special empty tagged type that can have additional cases added to it by external (dependent) packages.
This enables the creation of foreign key or metadata values, where the range of permissable types is defined by the users of an API.
package system type ExternalID xyz.Extern[ExternalID, string] package client var SystemExternalID xyz.AccessorFor(xyz.Extend[system.ExternalID, string, struct { Make xyz.Case[api.ExternalID, string] `json:"data?type=client.AccountID"` }].Values)
func (Extern) MarshalJSON ¶
func (Extern) MarshalPair ¶
MarshalPair based on the json field/type name and the storage value.
func (Extern) MarshalText ¶
func (Extern) Reflection ¶
func (v Extern) Reflection() []CaseReflection
func (*Extern) UnmarshalJSON ¶
func (*Extern) UnmarshalPair ¶
UnmarshalPair based on the json field/type name and the storage value.
func (*Extern) UnmarshalText ¶
type Maybe ¶
type Maybe[T any] map[ok]T
Maybe represents a value that is optional and can be omitted from the struct or function call it resides within. Not suitable for use as an underlying type.
func New ¶
New returns an un-omitted (included) Maybe value. Calls to [Get] will return this value and true.
func (Maybe[T]) Get ¶
Get returns the value and true if the value was included otherwise it returns the zero value and false.
func (Maybe[T]) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface.
func (*Maybe[T]) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface.
type Pair ¶
type Pair[X, Y any] struct { X X Y Y }
Pair holds two values.
func (Pair[X, Y]) MarshalJSON ¶
func (*Pair[X, Y]) UnmarshalJSON ¶
type Quad ¶
type Quad[X, Y, Z, W any] struct { X X Y Y Z Z W W }
Quad holds four values.
func (Quad[X, Y, Z, W]) Get ¶
func (q Quad[X, Y, Z, W]) Get() (X, Y, Z, W)
Get returns the values in the Quad.
func (Quad[X, Y, Z, W]) MarshalJSON ¶
func (Quad[X, Y, Z, W]) Split ¶
func (q Quad[X, Y, Z, W]) Split() (X, Y, Z, W)
Split returns the values in the Quad.
func (*Quad[X, Y, Z, W]) UnmarshalJSON ¶
type Static ¶
type Static[V isStatic[T], T any] struct { // contains filtered or unexported fields }
Static value that cannot be changed.
func (Static) MarshalJSON ¶
func (Static) UnmarshalJSON ¶
type Switch ¶
type Switch[Storage switchable, Values any] struct { // contains filtered or unexported fields }
Switch on the underlying Storage, which can be any one of the [Values].
func (*Switch) InterfaceAddr ¶
func (v *Switch) InterfaceAddr() any
func (Switch) MarshalText ¶
MarshalText implements encoding.TextMarshaler.
func (*Switch) RawPointer ¶
func (v *Switch) RawPointer() *Storage
func (*Switch) UnmarshalJSON ¶
UnmarshalJSON implements json.Unmarshaler.
func (*Switch) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler.
func (Switch) ValuesJSON ¶
func (v Switch) ValuesJSON() (oneof []json.RawMessage)
type Tagged ¶
Tagged union with the underlying storage in order to represent a restricted set of values. Can be used as the underlying value for a named type. Each case must be compatible with the memory storage representation.
func (Tagged) MarshalJSON ¶
func (Tagged) MarshalPair ¶
MarshalPair based on the json field/type name and the storage value.
func (Tagged) MarshalText ¶
func (Tagged) Reflection ¶
func (v Tagged) Reflection() []CaseReflection
func (*Tagged) UnmarshalJSON ¶
func (*Tagged) UnmarshalPair ¶
UnmarshalPair based on the json field/type name and the storage value.
func (*Tagged) UnmarshalText ¶
type Trio ¶
type Trio[X, Y, Z any] struct { X X Y Y Z Z }
Trio holds three values.
func (Trio[X, Y, Z]) Get ¶
func (t Trio[X, Y, Z]) Get() (X, Y, Z)
Get returns the values in the Trio.
func (Trio[X, Y, Z]) MarshalJSON ¶
func (Trio[X, Y, Z]) Split ¶
func (t Trio[X, Y, Z]) Split() (X, Y, Z)
Split returns the values in the Trio.