Skip to content
No description, website, or topics provided.
Go
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples
generator/builtins
generics
.gitignore
LICENSE
README.md
config.go
generator.go
main.go
util.go

README.md

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.

You can’t perform that action at this time.