goreify

command module
v0.0.0-...-fae6a60 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2021 License: MIT Imports: 20 Imported by: 0

README

This library is deprecated with Go 1.18 which now has support for generics natively.

goreify

goreify implements a form of generics for go. It works by taking in a function or type which uses dynamic functions and types and converts it into a function or type which uses concrete (reified) types.

For example, this generic function:

func Sum(xs []generics.T1) generics.T1 {
	var total generics.T1
	for _, x := range xs {
		total = generics.Add(total, x)
	}
	return total
}

Would be transformed into this reified version:

func Sum_int(xs []int) int {
	var total int
	for _, x := range xs {
		total = total + x
	}
	return total
}

The original, generic version is completely valid and runnable Go code. It's just less efficient because it relies on reflection, and a bit clunky to work with because we can't use operators.

More examples can be found below.

Installation

go get github.com/badgerodon/goreify

Usage

goreify takes two parameters, the input type/function, and the reified types you'd like to generate:

goreify path/to/your/package.YourFunction T1Type T2Type ...

For example:

goreify github.com/badgerodon/goreify/examples.Sum numeric

The special numeric pseudo-type is replaced with integers, floats and complex types.

In practice you should use go:generate lines in your code:

//go:generate goreify github.com/badgerodon/goreify/examples.Sum numeric

Supported Features

Global functions:

//go:generate goreify github.com/badgerodon/goreify/examples.Diff int

// Diff finds the difference between two series
func Diff(xs, ys []generics.T1) []generics.T1 {
	sz := len(xs)
	if len(ys) < sz {
		sz = len(ys)
	}

	zs := make([]generics.T1, sz)
	for i := 0; i < sz; i++ {
		zs[i] = generics.Subtract(xs[i], ys[i])
	}
	return zs
}

// yielding

func DiffInt(xs, ys []int) []int {
	sz := len(xs)
	if len(ys) < sz {
		sz = len(ys)
	}

	zs := make([]int, sz)
	for i := 0; i < sz; i++ {
		zs[i] = xs[i] - ys[i]
	}
	return zs
}

Types:

//go:generate goreify github.com/badgerodon/goreify/examples.Pair int8 int16

// A Pair is a pair of values
type Pair struct {
	// Fst is the first value
	Fst generics.T1
	// Snd is the second value
	Snd generics.T2
}

// yielding

type PairInt8Int16 struct {
	// Fst is the first value
	Fst int8
	// Snd is the second value
	Snd int16
}

Methods on types:

func (l *List) Append(els ...generics.T1) {
	l.elements = append(l.elements, els...)
}

// yielding

func (l *ListInt32) Append(els ...int32) {
	l.elements = append(l.elements, els...)
}

Global functions using a type:

func Zip(xs []generics.T1, ys []generics.T2) []Pair {
	mn := len(xs)
	if mn > len(ys) {
		mn = len(ys)
	}
	zs := make([]Pair, mn)
	for i := 0; i < mn; i++ {
		zs[i] = Pair{Fst: xs[i], Snd: ys[i]}
	}
	return zs
}

// yielding

func ZipInt8Int16(xs []int8, ys []int16) []PairInt8Int16 {
	mn := len(xs)
	if mn > len(ys) {
		mn = len(ys)
	}
	zs := make([]Pair_int8_int16, mn)
	for i := 0; i < mn; i++ {
		zs[i] = PairInt8Int16{Fst: xs[i], Snd: ys[i]}
	}
	return zs
}

Functions to work with channels:

//go:generate goreify github.com/badgerodon/goreify/examples.Merge int

func Merge(srcs ...<-chan generics.T1) <-chan generics.T1 {
	switch len(srcs) {
	case 0:
		return nil
	case 1:
		return srcs[0]
	case 2:
		dst := make(chan generics.T1)
		go func() {
			// merge until both channels are closed, then finally close the destination
			defer close(dst)

			c0, c1 := srcs[0], srcs[1]
			for c0 != nil || c1 != nil {
				select {
				case m, ok := <-c0:
					if !ok {
						c0 = nil
						continue
					}
					dst <- m
				case m, ok := <-c1:
					if !ok {
						c1 = nil
						continue
					}
					dst <- m
				}
			}
		}()
		return dst
	default:
		left, right := srcs[:len(srcs)/2], srcs[len(srcs)/2:]
		return Merge(Merge(left...), Merge(right...))
	}
}

// yielding

func MergeInt(srcs ...<-chan int) <-chan int {
	switch len(srcs) {
	case 0:
		return nil
	case 1:
		return srcs[0]
	case 2:
		dst := make(chan int)
		go func() {

			defer close(dst)

			c0, c1 := srcs[0], srcs[1]
			for c0 != nil || c1 != nil {
				select {
				case m, ok := <-c0:
					if !ok {
						c0 = nil
						continue
					}
					dst <- m
				case m, ok := <-c1:
					if !ok {
						c1 = nil
						continue
					}
					dst <- m
				}
			}
		}()
		return dst
	default:
		left, right := srcs[:len(srcs)/2], srcs[len(srcs)/2:]
		return MergeInt(MergeInt(left...), MergeInt(right...))
	}
}

Multiple dependent types: (for the input argument just put a , between the types)

//go:generate goreify github.com/badgerodon/goreify/examples.tree,treeNode int

type tree struct {
	root *treeNode
}

type treeNode struct {
	elem     generics.T1
	children []*treeNode
}

// yielding 

type treeInt struct {
	root *treeNodeInt
}

type treeNodeInt struct {
	elem     int
	children []*treeNodeInt
}

This is useful for many data structures that involve recursive pointers.

Other Libraries

I found gengen after implementing most of this functionality. It follows a similar approach, though doesn't take it quite as far.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
generator

Jump to

Keyboard shortcuts

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