Generic types and functions that are missing from Go, including sets, trees,
linked lists, etc.
All code is implemented with 0 dependencies and in pure Go code (no CGo).
Background
Go v1.18 is about to be released now in February 2022, and with it comes some
features that has been talked about for a really long time. One of which being
generics! (Go 1.18 beta release notes)
They have moved generics from the Go v2.0 milestone over to Go v1.18, which
means they have to stay backwards compatible and cannot alter any existing
types. On top of this, they do not seem to plan on releasing any generic data
types in the Go standard library until Go v1.19. All in all, to use generic
data types with Go v1.18, you’ll have to either write your own, or use a
third-party package, like this one :)
This repository includes those generic functions and types that I find are
missing from the release of Go v1.18-beta1, as well as a number of other
data structures and utility functions I think should’ve been included in the
standard library a long time ago. But now with generics, we can finally have
sensible implementations of sets, trees, stacks, etc without excessive casting.
Compatibility
Requires Go v1.18beta1 or later as the code makes use of generics.
Installation and usage
go get -u gopkg.in/typ.v1
func UsingSets() { set1 := make(typ.Set[string]) set1.Add("A") set1.Add("B") set1.Add("C") fmt.Println("set1:", set1) // {A B C} set2 := make(typ.Set[string]) set2.Add("B") set2.Add("C") set2.Add("D") fmt.Println("set2:", set2) // {B C D} fmt.Println("union:", set1.Union(set2)) // {A B C D} fmt.Println("intersect:", set1.Intersect(set2)) // {B C} fmt.Println("set diff:", set1.SetDiff(set2)) // {A} fmt.Println("sym diff:", set1.SymDiff(set2)) // {A D} } func UsingOrderedTree() { var tree typ.OrderedTree[string] // Unordered input tree.Add("E") tree.Add("B") tree.Add("D") tree.Add("C") tree.Add("A") // Sorted output fmt.Println(tree.Len(), tree) // 5 [A B C D E] }
Features
Types
typ.Array2D[T]
: 2-dimensional array.typ.AtomicValue[T]
: Atomic value store, wrapper aroundsync/atomic.Value
.typ.List[T]
: Linked list, forked fromcontainer/list
.typ.Null[T]
: Nullable type without needing pointers, forked fromgithub.com/volatiletech/null/v9
typ.OrderedSlice[T]
: Always-sorted slice for ordered types.typ.OrderedTree[T]
: AVL-tree (auto-balancing binary search tree) implementation for ordered types.typ.Pool[T]
: Object pool, wrapper aroundsync.Pool
.typ.Publisher[T]
: Publish-subscribe pattern (pubsub) using channels.typ.Queue[T]
: First-in-first-out collection.typ.Ring[T]
: Circular list, forked fromcontainer/ring
.typ.Set[T]
: Set, based on set theory.typ.SortedSlice[T]
: Always-sorted slice. Requires customless
function.typ.Stack[T]
: First-in-last-out collection.typ.SyncMap[K,V]
: Concurrent map, forked fromsync.Map
.
Explanation:
Forked type: Copied their code and modified it so it uses generic types down
to the backing struct layer. This benefits the most from generics support.Wrapped type: Code depends on the underlying non-generic type, and adds
abstraction to hide the type casting. Less performant than full generic
support, but is done to reduce excessive complexity in this repository.Neither forked nor wrapped: Original code written by yours truly.
Constraints
typ.Number
: Type constraint for any number: integers, floats, & complex.typ.Real
: Type constraint for real numbers: integers & floats.
Utility functions
typ.All[T]([]T, func(T) bool) bool
: Does condition match all values?typ.Any[T]([]T, func(T) bool) bool
: Does condition match any value?typ.ChunkIter[T]([]T, int) [][]T
: Invoke callback for all chunks in a slice.typ.Chunk[T]([]T, int) [][]T
: Divide up a slice.typ.Clamp01[T](T) T
: Clamp a value between0
and1
.typ.Clamp[T](T, T, T) T
: Clamp a value inside a range.typ.Coal[T](...T) T
: Coalesce operator, returns first non-zero value.typ.ContainsFunc[T]([]T, T, func(T, T) bool) bool
: Checks if value exists in slice with custom equals.typ.ContainsValue[K, V](map[K]V, V) bool
: Does map contain value?typ.Contains[T]([]T, T) bool
: Does slice contain value?typ.CountBy[K, V]([]V, func(V) K) []Counting[K]
: Count elements by key.typ.DistinctFunc[T]([]T, func(T, T) bool) []T
: Returns new slice of unique elements with custom equals.typ.Distinct[T]([]T, func(T, T) bool) []T
: Returns new slice of unique elements.typ.ExceptSet[T]([]T, Set[T]) []T
: Exclude values from other set.typ.Except[T]([]T, []T) []T
: Exclude values from other slice.typ.Fill[T]([]T, T)
: Fill a slice with a value.typ.Filter[T](slice []T, func(T) bool) []T
: Returns filtered slice.typ.FoldReverse[TState, T]([]T, TState, func(TState, T) TState) TState
: Accumulate values from slice in reverse order.typ.Fold[TState, T]([]T, TState, func(TState, T) TState) TState
: Accumulate values from slice.typ.GroupBy[K, V]([]V, func(V) K) []Grouping[K, V]
: Group elements by key.typ.IndexFunc[T]([]T, func(T) bool) int
: Returns index of a value, or -1 if not found.typ.Index[T]([]T, T) int
: Returns index of a value, or -1 if not found.typ.InsertSlice[T](*[]T, int, []T)
: Inserts a slice of values at index.typ.Insert[T](*[]T, int, T)
: Inserts a value at index.typ.IsNil[T](T) bool
: Returns true if the generic value is nil.typ.Last[T]([]T) T
: Returns the last item in a slice.typ.MakeChanOfChan[T](chan T, ...int) chan T
: Returns the result ofmake(chan T)
, useful for anonymous types.typ.MakeChanOf[T](T, ...int) chan T
: Returns the result ofmake(chan T)
, useful for anonymous types.typ.MakeMapOfMap[K,V](map[K]V, ...int) map[K]V
: Returns the result ofmake(map[K]V)
, useful for anonymous types.typ.MakeMapOf[K,V](K, V, ...int) map[K]V
: Returns the result ofmake(map[K]V)
, useful for anonymous types.typ.MakeSliceOfKey[K,V](map[K]V, ...int) []K
: Returns the result ofmake([]K)
, useful for anonymous types.typ.MakeSliceOfSlice[T]([]T, ...int) []T
: Returns the result ofmake([]T)
, useful for anonymous types.typ.MakeSliceOfValue[K,V](map[K]V, ...int) []V
: Returns the result ofmake([]V)
, useful for anonymous types.typ.MakeSliceOf[T](T, ...