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