xyz

package
v0.0.0-...-2480c0b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2025 License: 0BSD Imports: 13 Imported by: 1

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func AccessorFor

func AccessorFor[S any, T any, V func(S, internal) T](values V) T

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.

func Raw

func Raw[T switchWith[Storage, Values], Storage any, Values any](val Storage) T

Raw returns a switch value of the given type, with the given storage.

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.

func (Case) Key

func (v Case) Key() (string, error)

Key returns the key for this case, as if it were returned by MarshalPair.

func (Case) New

func (v Case) New(val Constraint) Variant

func (Case) String

func (v Case) String() string

func (Case) With

func (v Case) With(val Constraint) Variant

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...)

func (Duplex[T]) String

func (d Duplex[T]) String() string

String returns the string representation of the Duplex number.

func (Duplex[T]) Values

func (d Duplex[T]) Values() []T

Values returns each value represented by the Duplex number. ie

  • a ± b returns a+b,a-b
  • a ± b ± c returns a+b+c, a+b-c, a-b+c, 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) Get

func (v Extend) Get() (Storage, bool)

func (Extend) Interface

func (v Extend) Interface() any

func (Extend) MarshalJSON

func (v Extend) MarshalJSON() ([]byte, error)

func (Extend) MarshalPair

func (v Extend) MarshalPair() (Pair[string, Storage], error)

MarshalPair based on the json field/type name and the storage value.

func (Extend) MarshalText

func (v Extend) MarshalText() ([]byte, error)

func (Extend) Reflection

func (v Extend) Reflection() []CaseReflection

func (Extend) String

func (v Extend) String() string

String implements fmt.Stringer.

func (Extend) Tag

func (v Extend) Tag() reflect.StructTag

func (*Extend) UnmarshalJSON

func (v *Extend) UnmarshalJSON(data []byte) error

func (*Extend) UnmarshalPair

func (v *Extend) UnmarshalPair(pair Pair[string, Storage]) error

UnmarshalPair based on the json field/type name and the storage value.

func (*Extend) UnmarshalText

func (v *Extend) UnmarshalText(data []byte) error

func (Extend) Validate

func (Extend) Validate(val any) bool

func (Extend) Values

func (v Extend) Values(internal) Values

type Extern

type Extern[T any, Storage any] struct {
	// contains filtered or unexported fields
}

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) Get

func (v Extern) Get() (Storage, bool)

func (Extern) Interface

func (v Extern) Interface() any

func (Extern) MarshalJSON

func (v Extern) MarshalJSON() ([]byte, error)

func (Extern) MarshalPair

func (v Extern) MarshalPair() (Pair[string, Storage], error)

MarshalPair based on the json field/type name and the storage value.

func (Extern) MarshalText

func (v Extern) MarshalText() ([]byte, error)

func (Extern) Reflection

func (v Extern) Reflection() []CaseReflection

func (Extern) String

func (v Extern) String() string

String implements fmt.Stringer.

func (Extern) Tag

func (v Extern) Tag() reflect.StructTag

func (*Extern) UnmarshalJSON

func (v *Extern) UnmarshalJSON(data []byte) error

func (*Extern) UnmarshalPair

func (v *Extern) UnmarshalPair(pair Pair[string, Storage]) error

UnmarshalPair based on the json field/type name and the storage value.

func (*Extern) UnmarshalText

func (v *Extern) UnmarshalText(data []byte) error

func (Extern) Validate

func (Extern) Validate(val any) bool

func (Extern) Values

func (v Extern) Values(internal) Values

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

func New[T any](val T) Maybe[T]

New returns an un-omitted (included) Maybe value. Calls to [Get] will return this value and true.

func (Maybe[T]) Get

func (o Maybe[T]) Get() (T, bool)

Get returns the value and true if the value was included otherwise it returns the zero value and false.

func (Maybe[T]) MarshalJSON

func (o Maybe[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface.

func (Maybe[T]) TypeJSON

func (o Maybe[T]) TypeJSON() reflect.Type

func (*Maybe[T]) UnmarshalJSON

func (o *Maybe[T]) UnmarshalJSON(b []byte) error

UnmarshalJSON implements the json.Unmarshaler interface.

type Pair

type Pair[X, Y any] struct {
	X X
	Y Y
}

Pair holds two values.

func NewPair

func NewPair[X, Y any](x X, y Y) Pair[X, Y]

NewPair returns a new Pair from the given values.

func (Pair[X, Y]) Get

func (p Pair[X, Y]) Get() (X, Y)

Get returns the values in the Pair.

func (Pair[X, Y]) MarshalJSON

func (p Pair[X, Y]) MarshalJSON() ([]byte, error)

func (Pair[X, Y]) Split

func (p Pair[X, Y]) Split() (X, Y)

Split returns the values in the Pair.

func (*Pair[X, Y]) UnmarshalJSON

func (p *Pair[X, Y]) UnmarshalJSON(data []byte) error

type Quad

type Quad[X, Y, Z, W any] struct {
	X X
	Y Y
	Z Z
	W W
}

Quad holds four values.

func NewQuad

func NewQuad[X, Y, Z, W any](a X, b Y, c Z, d W) Quad[X, Y, Z, W]

NewQuad returns a new Quad from the given 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 (q Quad[X, Y, Z, W]) MarshalJSON() ([]byte, error)

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

func (q *Quad[X, Y, Z, W]) UnmarshalJSON(data []byte) error

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 (v Static) MarshalJSON() ([]byte, error)

func (Static[V, T]) TypeJSON

func (s Static[V, T]) TypeJSON() reflect.Type

func (Static) UnmarshalJSON

func (v Static) UnmarshalJSON(data []byte) error

func (Static) Value

func (v Static) Value() T

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) GobDecode

func (v *Switch) GobDecode(data []byte) error

func (Switch) GobEncode

func (v Switch) GobEncode() ([]byte, error)

func (Switch) Interface

func (v Switch) Interface() any

func (*Switch) InterfaceAddr

func (v *Switch) InterfaceAddr() any

func (Switch) MarshalText

func (v Switch) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Switch) Raw

func (v Switch) Raw() Storage

Raw returns the underlying storage value.

func (*Switch) RawPointer

func (v *Switch) RawPointer() *Storage

func (Switch) String

func (v Switch) String() string

String implements fmt.Stringer.

func (*Switch) UnmarshalJSON

func (v *Switch) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

func (*Switch) UnmarshalText

func (v *Switch) UnmarshalText(data []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (Switch) Values

func (v Switch) Values(internal) Values

func (Switch) ValuesJSON

func (v Switch) ValuesJSON() (oneof []json.RawMessage)

type Tagged

type Tagged[Storage any, Values any] struct {
	// contains filtered or unexported fields
}

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) Get

func (v Tagged) Get() (Storage, bool)

func (Tagged) Interface

func (v Tagged) Interface() any

func (Tagged) MarshalJSON

func (v Tagged) MarshalJSON() ([]byte, error)

func (Tagged) MarshalPair

func (v Tagged) MarshalPair() (Pair[string, Storage], error)

MarshalPair based on the json field/type name and the storage value.

func (Tagged) MarshalText

func (v Tagged) MarshalText() ([]byte, error)

func (Tagged) Reflection

func (v Tagged) Reflection() []CaseReflection

func (Tagged) String

func (v Tagged) String() string

String implements fmt.Stringer.

func (Tagged) Tag

func (v Tagged) Tag() reflect.StructTag

func (*Tagged) UnmarshalJSON

func (v *Tagged) UnmarshalJSON(data []byte) error

func (*Tagged) UnmarshalPair

func (v *Tagged) UnmarshalPair(pair Pair[string, Storage]) error

UnmarshalPair based on the json field/type name and the storage value.

func (*Tagged) UnmarshalText

func (v *Tagged) UnmarshalText(data []byte) error

func (Tagged) Validate

func (Tagged) Validate(val any) bool

func (Tagged) Values

func (v Tagged) Values(internal) Values

type Trio

type Trio[X, Y, Z any] struct {
	X X
	Y Y
	Z Z
}

Trio holds three values.

func NewTrio

func NewTrio[X, Y, Z any](a X, b Y, c Z) Trio[X, Y, Z]

NewTrio returns a new Trio from the given 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 (t Trio[X, Y, Z]) MarshalJSON() ([]byte, error)

func (Trio[X, Y, Z]) Split

func (t Trio[X, Y, Z]) Split() (X, Y, Z)

Split returns the values in the Trio.

func (*Trio[X, Y, Z]) UnmarshalJSON

func (t *Trio[X, Y, Z]) UnmarshalJSON(data []byte) error

type TypeOf

type TypeOf[T any] interface {
	fmt.Stringer

	Key() (string, error)
	// contains filtered or unexported methods
}

TypeOf represents the type of a field within a variant.

func ValueOf

func ValueOf[Storage any, Values any, Variant taggedWith[Storage, Values]](variant Variant) TypeOf[Variant]

ValueOf returns the value of the switch. Typically used as the expression in a switch statement.

type Validator

type Validator interface {
	Validate(any) bool
}

type When

type When[Variant isVariant, Constraint Validator] struct {
	// contains filtered or unexported fields
}

func (When[Variant, Constraint]) String

func (v When[Variant, Constraint]) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL