Skip to content

Instantly share code, notes, and snippets.

@dotaheor
Last active February 17, 2019 03:48
Show Gist options
  • Save dotaheor/c805d221ed86265d6e8bb4f16a714060 to your computer and use it in GitHub Desktop.
Save dotaheor/c805d221ed86265d6e8bb4f16a714060 to your computer and use it in GitHub Desktop.

This is an enhanced version of my old generic proposal.

This proposal suggests a gen code element which can be viewed as a compile-time code generation function call. The parameters and results of a gen can be any source elements, such as type, const, var, func, import, even gen.

This proposal will use the contract idea in the Go 2 generic draft to constraint input gen parameters. It will also use the following helpers:

- `typeOf(v)` // return the type (or default type) of value `v`.
- `underlying(T)` // return the underlying type of `T`
- `element(T)` // return the element type of `T`, `T` must be an array, slice, map, channel type
- `key(T)` // return the key type of map `T`
- `base(T)` // return the base type of pointer `T`
- `field(T.f)` // return the field type of struct `T`
- `method(T.f)` // return the method type of `T`
- `selector(T.f)` // `T.f` is either of a field (of a function type) or a method
- `input(T.n)` // return the *n*th parameter type of a function type, *n* is an unsigned integer.
- `output(T.n)` // return the *n*th result type of a function type, *n* is an unsigned integer.
- `numInputs(T)` // return the number of parameters of a function type
- `numOutputs(T)` // return the number of results of a function type
- `length(T)` // return the length of an array type

An example (single func export):

package convert

contract convertible(_ To, f From) {
	To(f)
}

gen ConvertSlice[Slice, NewElement type] [func] {
	// assure is a keyword can be used in generic declarations
	assure convertible(NewElement, element(Slice))
	
	func Convert(x Slice) []NewElement {
		if x == nil {
			return nil
		}
		y := make([]NewElement, 0, len(x))
		for i := range x {
			y = append(y, NewElement(x[i]))
		}
		return y
	}
	
	export Convert
}

//======================================

package main

import "convert"

func stringSlice2InterfaceSlice = convert.ConvertSlice[[]string, interfacce{}]

func main() {
	words := []string{"hello", "bye"}
	fmt.Println(stringSlice2InterfaceSlice(words)...)
	
	// or, let compiler deduce the first argument of the generic call.
	_ = convert.ConvertSlice[, interfacce{}](words)
}

Another example (single type export):

package list

import "fmt"

// this generic has not any constraints for type T.
gen List[T type] type {
	type node struct {
		Element T
		Next    *node
	}
	
	func (n *node) Push(e T) *node {
		newNode := &node{Element: e}
		if node == nil {
			return newNode
		}
		node.Next = newNode
		return node
	}
	
	func (n *node) Dump() {
		fmt.Print("Dump result: ")
		for n != nil {
			fmt.Print(n.Element)
			if n.Next != nil {
				fmt.Print(", ")
			}
			n = n.Next
		}
		fmt.Println()
	}
	
	export node
}

//======================================

package main

import "list"

func main() {
	var intList *lib2.List[int]
	intList = intList.Push(123)
	intList = intList.Push(456)
	intList = intList.Push(789)
	intList.Dump()
	
	var strList *lib2.List[string]
	strList = intList.Push("abc")
	strList = intList.Push("mno")
	strList = intList.Push("xyz")
	strList.Dump()
}

A more complex example (a single import return):

package mypkg

gen Example[] [import] {
	type T struct{}
	func F(T) {}
	
	export {
		Foo: F,
		Bar: T,
	}
}

//======================================

package main

import "mypkg"

import alib = mypkg.Example[]

func main() {
	var v alib.Bar
	alib.Foo(v)
}

A gen returns another gen

package treemap

gen TreeMap[Key type] [gen] {
	gen trMap[Element type] type {
		type Tree struct {...}
		func (t *Tree) Put(k Key, e Element) {...}
		func (t *Tree) Get(k Key) Element {...}
		func (t *Tree) Has(k Key) bool {...}
		func (t *Tree) Delete(k Key)(Element, bool) {...}
		
		export Tree
	}
	
	export trMap
}

//======================================

package main

import "treemap"

type TreeMap = treemap.TreeMap[string][int]

func main() {
	var tm TreeMap
	tm.Put("Go", 2009)
	...
}

By this way, it is possible to unify built-in generics and custom generics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment