This document references the Contracts Draft Design for Go Generics, dated 31 July 2019.
The current design uses parentheses to delimit type parameter clauses, but this makes the code difficult to read. This is covered briefly in Non-Parenthetical Delimiters from the essay Towards Clarity: Syntax Changes for Contracts in Go, as well as in many other points of feedback which can be found at https://github.com/golang/go/wiki/Go2GenericsFeedback as linked documents or inline comments.
The official draft states:
Why not use the syntax
F<T>
like C++ and Java?
When parsing code within a function, such asv := F<T>
, at the point of seeing the < it's ambiguous whether we are seeing a type instantiation or an expression using the < operator. Resolving that requires effectively unbounded lookahead. In general we strive to keep the Go parser simple.
I propose a solution to the <
ambiguity by adopting the following rules:
- The
<>
characters are used to delimit type parameter clauses. - In function, method and type declarations, the form used is
identifier<types...>
. - In bare type instantiations, the form used is
identifier<type>
. - In function calls, the form used is
function(<types...> arguments...)
. - In struct literals, the form used is
struct{<types...> field: value...)
. - In type conversions, the form used is
to(<types...> from)
More generally, where a type is expected, the identifier<type>
form should be unambiguous because a comparison operation makes no sense.
It seems as though a lesser-than operator would never appear in the following:
- A type declaration
- A type alias
- A contract
- A
var
statement - A function/method declaration
- A type assertion
In other words, and especially given inference of type parameters, it seems that there are few scenarios where F<T>
could be confused with i < j
—mainly something that will evaluate and then be stored in a variable.
Some examples where a comparison would be unexpected:
var v Vector<int>
type List<type Element> struct {
next *List<Element>
val Element
}
type VectorInt = Vector<int>
contract PrintStringer<X> {
stringer<X>
X Print()
}
contract G<Node, Edge> {
Node Edges() []Edge
Edge Nodes() (from Node, to Node)
}
type Graph<type Node, Edge G> struct { ... }
func New<type Node, Edge G>(nodes []Node) *Graph(Node, Edge) { ... }
func (g *Graph<Node, Edge>) ShortestPath(from, to Node) []Edge { ... }
On the other hand, here are some examples of how it would work with function calls, struct literals, etc:
func Ranger<type T>() (*Sender<T>, *Receiver<T>) {
c := make(chan T)
d := make(chan bool)
s := &Sender{<T> values: c, done: d}
r := &Receiver{<T> values: c, done: d}
runtime.SetFinalizer(r, r.finalize)
return s, r
}
func New<type K, V>(compare func(K, K) int) *Map<K, V> {
return &Map{<K, V> compare: compare}
}
func (m *Map<K, V>) InOrder() *Iterator<K, V> {
sender, receiver := chans.Ranger(<keyValue<K, V>>)
var f func(*node<K, V>) bool
f = func(n *node<K, V>) bool {
if n == nil {
return true
}
// Stop sending values if sender.Send returns false,
// meaning that nothing is listening at the receiver end.
return f(n.left) &&
sender.Send(keyValue{<K, V> n.key, n.val}) &&
f(n.right)
}
go func() {
f(m.root)
sender.Close()
}()
return &Iterator{receiver}
}
(See below for a full code comparison with the existing Contracts draft.)
It's open to opinion, but perhaps this even makes the code more readable than if all type parameters immediately followed the relevant identifier. Consider which is more readable:
sender, receiver := chans.Ranger<keyValue<K, V>>()
sender.Send(keyValue<K, V>{n.key, n.val})
// or
sender, receiver := chans.Ranger(<keyValue<K, V>>)
sender.Send(keyValue{<K, V> n.key, n.val})
By putting the type parameters inside the ()
or {}
it seems a little easier to visually identify Ranger
as a method and keyValue
as a struct literal.
In summary, this would resolve the ambiguity of:
a := b<x, y con>(j, k) // parser: "b < x"?
by changing it to this:
a := b(<x, y con> j, k) // parser: type parameters ahoy!
(Note: Towards Clarity: Syntax Changes for Contracts in Go contains several other concepts such as the empty contract, an alternate contract syntax, and an alternate type parameter syntax, all of which I maintain are valid, but they were omitted from this document to preserve the focus.
// Print prints the elements of a slice.
// It should be possible to call this with any slice value.
func Print(s []T) { // Just an example, not the suggested syntax.
for _, v := range s {
fmt.Println(v)
}
}
func Print<type T>(s []T) {
// same as above
}
Print(<int> []int{1, 2, 3})
// This function is INVALID.
func Stringify<type T>(s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String()) // INVALID
}
return ret
}
contract stringer<T> {
T String() string
}
func Stringify<type T stringer>(s []T) (ret []string) {
for _, v := range s {
ret = append(ret, v.String()) // now valid
}
return ret
}
func Print2<type T1, T2>(s1 []T1, s2 []T2) { ... }
func Print2Same<type T1>(s1 []T1, s2 []T1) { ... }
contract viaStrings<To, From> {
To Set(string)
From String() string
}
func SetViaStrings<type To, From viaStrings>(s []From) []To {
r := make([]To, len(s))
for i, v := range s {
r[i].Set(v.String())
}
return r
}
type Vector<type Element> []Element
var v Vector<int>
func (v *Vector<Element>) Push(x Element) { *v = append(*v, x) }
// This is OK.
type List<type Element> struct {
next *List<Element>
val Element
}
// This type is INVALID.
type P<type Element1, Element2> struct {
F *P<Element2, Element1> // INVALID; must be (Element1, Element2)
}
type StringableVector<type T stringer> []T
func (s StringableVector<T>) String() string {
var sb strings.Builder
sb.WriteString("[")
for i, v := range s {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(v.String())
}
sb.WriteString("]")
return sb.String()
}
type Lockable<type T> struct {
T
mu sync.Mutex
}
func (l *Lockable<T>) Get() T {
l.mu.Lock()
defer l.mu.Unlock()
return l.T
}
type VectorInt = Vector<int>
contract PrintStringer<X> {
stringer<X>
X Print()
}
contract PrintStringer<X> {
X String() string
X Print()
}
package compare
// The equal contract describes types that have an Equal method with
// an argument of the same type as the receiver type.
contract equal<T> {
T Equal(T) bool
}
// Index returns the index of e in s, or -1.
func Index<type T equal>(s []T, e T) int {
for i, v := range s {
// Both e and v are type T, so it's OK to call e.Equal(v).
if e.Equal(v) {
return i
}
}
return -1
}
import "compare"
type EqualInt int
// The Equal method lets EqualInt satisfy the compare.equal contract.
func (a EqualInt) Equal(b EqualInt) bool { return a == b }
func Index(s []EqualInt, e EqualInt) int {
return compare.Index(<EqualInt> s, e)
}
package graph
contract G<Node, Edge> {
Node Edges() []Edge
Edge Nodes() (from Node, to Node)
}
type Graph<type Node, Edge G> struct { ... }
func New<type Node, Edge G>(nodes []Node) *Graph(Node, Edge) { ... }
func (g *Graph<Node, Edge>) ShortestPath(from, to Node) []Edge { ... }
type Vertex struct { ... }
func (v *Vertex) Edges() []*FromTo { ... }
type FromTo struct { ... }
func (ft *FromTo) Nodes() (*Vertex, *Vertex) { ... }
var g = graph.New(<*Vertex, *FromTo> []*Vertex{ ... })
type NodeInterface interface { Edges() []EdgeInterface }
type EdgeInterface interface { Nodes() (NodeInterface, NodeInterface) }
func MapAndPrint<type E, M stringer(M)>(s []E, f(E) M) []string {
r := make([]string, len(s))
for i, v := range s {
r[i] = f(v).String()
}
return r
}
package from
contract setter<T> {
T Set(string) error
}
func Strings<type T setter>(s []string) ([]T, error) {
ret := make([]T, len(s))
for i, v := range s {
if err := ret[i].Set(v); err != nil {
return nil, err
}
}
return ret, nil
}
type Settable int
func (p *Settable) Set(s string) (err error) {
*p, err = strconv.Atoi(s)
return err
}
func F() {
// The type of nums is []Settable.
nums, err := from.Strings(<Settable> []string{"1", "2"})
if err != nil { ... }
// Settable can be converted directly to int.
// This will set first to 1.
first := int(nums[0])
...
}
package pair
type Pair<type carT, cdrT> struct {
f1 carT
f2 cdrT
}
Print(<int> []int{1, 2, 3})
s1 := []int{1, 2, 3}
Print(s1)
package transform
func Slice<type From, To>(s []From, f func(From) To) []To {
r := make([]To, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
strs := transform.Slice([]int{1, 2, 3}, strconv.Itoa)
package pair
func New<type T>(f1, f2 T) *Pair(T) { ... }
type Pair<type T> struct { f1, f2 T }
var V = Pair{1, 2} // inferred as Pair{<int> 1, 2}
// PrintInts will be type func([]int).
var PrintInts = Print<int>
contract reader<T> {
T Read([]byte) (int, error)
}
func ReadByte<type T reader>(r T) (byte, error) {
if br, ok := r.(io.ByteReader); ok {
return br.ReadByte()
}
var b [1]byte
_, err := r.Read(b[:])
return b[0], err
}
x1 := []T(v1) // type conversion
x2 := []T{<v2>} // composite literal of type []T<v2>
var f func(x(T)) // named parameter of type (T)
var g func(x<T>) // unnamed parameter of type x<T>
type S1<type T> struct {
f T
}
type S2 struct {
S1(int) // field named S1 of type (int)
}
type S3 struct {
S1<int> // embedded instantiated type of S1<int>
}
type I1<type T> interface {
M(T)
}
type I2 interface {
I1(int) // method named I1 with argument of type int
}
type I3 interface {
I1<int> // embedded instantiated type of I1<int>
}
contract setter<T> {
T Set(string)
}
func Init<type T setter>(s string) T {
var r T
r.Set(s)
return r
}
type MyInt int
func (p *MyInt) Set(s string) {
v, err := strconv.Atoi(s)
if err != nil {
log.Fatal("Init failed", err)
}
*p = MyInt(v)
}
// INVALID
// MyInt does not have a Set method, only *MyInt has one.
var Init1 = Init(<MyInt> "1")
// DOES NOT WORK
// r in Init is type *MyInt with value nil,
// so the Set method does a nil pointer deference.
var Init2 = Init(<*MyInt> "2")
contract setter<T> {
*T Set(string)
}
func LookupAsString<type T stringer>(m map[int]T, k int) string {
return m[k].String() // Note: calls method on value of type T
}
type MyInt int
func (p *MyInt) String() { return strconv.Itoa(int(*p)) }
func F(m map[int]MyInt) string {
return LookupAsString(MyInt)(m, 0)
}
// This function is INVALID.
func Smallest<type T>(s []T) T {
r := s[0] // panics if slice is empty
for _, v := range s[1:] {
if v < r { // INVALID
r = v
}
}
return r
}
contract SignedInteger<T> {
T int, int8, int16, int32, int64
}
contract Ordered<T> {
T int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64,
string
}
func Smallest<type T Ordered>(s []T) T {
r := s[0] // panics if slice is empty
for _, v := range s[1:] {
if v < r {
r = v
}
}
return r
}
// PrintStringer1 and PrintStringer2 are equivalent.
contract PrintStringer1<T> {
T String() string
T Print()
}
contract PrintStringer2<T> {
T String() string; T Print()
}
contract Float<T> {
T float32, float64
}
contract IOCloser<S> {
S Read([]byte) (int, error), // note trailing comma
Write([]byte) (int, error)
S Close() error
}
contract unsatisfiable<T> {
T int
T uint
}
contract StringableSignedInteger<T> {
T int, int8, int16, int32, int64
T String() string
}
type MyInt int
func (mi MyInt) String() string {
return fmt.Sprintf("MyInt(%d)", mi)
}
contract byteseq<T> {
T string, []byte
}
type MyByte byte
type MyByteAlias = byte
func Join<type T byteseq>(a []T, sep T) (ret T) {
if len(a) == 0 {
// Use the result parameter as a zero value;
// see discussion of zero value below.
return ret
}
if len(a) == 1 {
return T(append([]byte(nil), a[0]...))
}
n := len(sep) * (len(a) - 1)
for i := 0; i < len(a); i++ {
n += len(a[i]) // len works for both string and []byte
}
b := make([]byte, n)
bp := copy(b, a[0])
for _, s := range a[1:] {
bp += copy(b[bp:], sep)
bp += copy(b[bp:], s)
}
return T(b)
}
contract Slice<S, Element> {
S []Element
}
func Map<type S, Element Slice>(s S, f func(Element) Element) S {
r := make(S, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
type MySlice []int
func DoubleMySlice(s MySlice) MySlice {
v := Map(<MySlice, int> s, func(e int) int { return 2 * e })
// Here v has type MySlice, not type []int.
return v
}
type M<type T> []T
contract C<T> {
T M(T) // T must implement the method M with an argument of type T
T M<T> // T must be the type M(T)
}
func Index<type T comparable>(s []T, x T) int {
for i, v := range s {
if v == x {
return i
}
}
return -1
}
contract integer<T> {
T int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr
}
contract integer2<T1, T2> {
integer<T1>
integer<T2>
}
func Convert<type To, From integer2>(from From) To {
to := To(from)
if From(to) != from {
panic("conversion out of range")
}
return to
}
contract integer<T> {
T int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr
}
func Add10<type T integer>(s []T) {
for i, v := range s {
s[i] = v + 10 // OK: 10 can convert to any integer type
}
}
// This function is INVALID.
func Add1024<type T integer>(s []T) {
for i, v := range s {
s[i] = v + 1024 // INVALID: 1024 not permitted by int8/uint8
}
}
type Optional<type T> struct {
p *T
}
func (o Optional<T>) Val() T {
if o.p != nil {
return *o.p
}
var zero T
return zero
}
F(<int, float64> x, y)(s)
func Map<type Element>(s []Element, f func(Element) Element) []Element {
r := make([]Element, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
type MySlice []int
func DoubleMySlice(s MySlice) MySlice {
s2 := Map(s, func(e int) int { return 2 * e })
// Here s2 is type []int, not type MySlice.
return MySlice(s2)
}
contract Float<F> {
F float32, float64
}
func NewtonSqrt<type F Float>(v F) F {
var iterations int
switch v.(type) {
case float32:
iterations = 4
case float64:
iterations = 5
default:
panic(fmt.Sprintf("unexpected type %T", v))
}
// Code omitted.
}
type MyFloat float32
var G = NewtonSqrt(MyFloat(64))
type orderedSlice<type Elem Ordered> []Elem
func (s orderedSlice<Elem>) Len() int { return len(s) }
func (s orderedSlice<Elem>) Less(i, j int) bool { return s[i] < s[j] }
func (s orderedSlice<Elem>) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// OrderedSlice sorts the slice s in ascending order.
// The elements of s must be ordered using the < operator.
func OrderedSlice<type Elem Ordered>(s []Elem) {
sort.Sort(orderedSlice(<Elem> s))
}
sort.OrderedSlice(<int32> []int32{3, 5, 2})
sort.OrderedSlice([]string{"a", "c", "b"})
type sliceFn<type Elem> struct {
s []Elem
f func(Elem, Elem) bool
}
func (s sliceFn<Elem>) Len() int { return len(s.s) }
func (s sliceFn<Elem>) Less(i, j int) bool { return s.f(s.s[i], s.s[j]) }
func (s sliceFn<Elem>) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
// SliceFn sorts the slice s according to the function f.
func SliceFn<type Elem>(s []Elem, f func(Elem, Elem) bool) {
Sort(sliceFn{<Elem> s, f})
}
var s []*Person
// ...
sort.SliceFn(s, func(p1, p2 *Person) bool { return p1.Name < p2.Name })
package maps
// Keys returns the keys of the map m.
// Note that map keys (here called type K) must be comparable.
func Keys<type K, V comparable(K)>(m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
k := maps.Keys(map[int]int{1:2, 2:4}) // sets k to []int{1, 2} (or {2, 1})
// Package slices implements various slice algorithms.
package slices
// Map turns a []T1 to a []T2 using a mapping function.
func Map<type T1, T2>(s []T1, f func(T1) T2) []T2 {
r := make([]T2, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
// Reduce reduces a []T1 to a single value using a reduction function.
func Reduce<type T1, T2>(s []T1, initializer T2, f func(T2, T1) T2) T2 {
r := initializer
for _, v := range s {
r = f(r, v)
}
return r
}
// Filter filters values from a slice using a filter function.
func Filter<type T>(s []T, f func(T) bool) []T {
var r []T
for _, v := range s {
if f(v) {
r = append(r, v)
}
}
return r
}
s := []int{1, 2, 3}
floats := slices.Map(s, func(i int) float64 { return float64(i) })
sum := slices.Reduce(s, 0, func(i, j int) int { return i + j })
evens := slices.Filter(s, func(i int) bool { return i%2 == 0 })
// Package set implements sets of any type.
package set
type Set<type Elem comparable> map[Elem]struct{}
func Make<type Elem comparable>() Set<Elem> {
return make(Set<Elem>)
}
func (s Set<Elem>) Add(v Elem) {
s[v] = struct{}{}
}
func (s Set<Elem>) Delete(v Elem) {
delete(s, v)
}
func (s Set<Elem>) Contains(v Elem) bool {
_, ok := s[v]
return ok
}
func (s Set<Elem>) Len() int {
return len(s)
}
func (s Set<Elem>) Iterate(f func(Elem)) {
for v := range s {
f(v)
}
}
Example use:
s := set.Make(<int>)
s.Add(1)
if s.Contains(2) { panic("unexpected 2") }
package chans
import "runtime"
// Ranger returns a Sender and a Receiver. The Receiver provides a
// Next method to retrieve values. The Sender provides a Send method
// to send values and a Close method to stop sending values. The Next
// method indicates when the Sender has been closed, and the Send
// method indicates when the Receiver has been freed.
//
// This is a convenient way to exit a goroutine sending values when
// the receiver stops reading them.
func Ranger<type T>() (*Sender<T>, *Receiver<T>) {
c := make(chan T)
d := make(chan bool)
s := &Sender{<T> values: c, done: d}
r := &Receiver{<T> values: c, done: d}
runtime.SetFinalizer(r, r.finalize)
return s, r
}
// A sender is used to send values to a Receiver.
type Sender<type T> struct {
values chan<- T
done <-chan bool
}
// Send sends a value to the receiver. It returns whether any more
// values may be sent; if it returns false the value was not sent.
func (s *Sender<T>) Send(v T) bool {
select {
case s.values <- v:
return true
case <-s.done:
return false
}
}
// Close tells the receiver that no more values will arrive.
// After Close is called, the Sender may no longer be used.
func (s *Sender<T>) Close() {
close(s.values)
}
// A Receiver receives values from a Sender.
type Receiver<type T> struct {
values <-chan T
done chan<- bool
}
// Next returns the next value from the channel. The bool result
// indicates whether the value is valid, or whether the Sender has
// been closed and no more values will be received.
func (r *Receiver<T>) Next() (T, bool) {
v, ok := <-r.values
return v, ok
}
// finalize is a finalizer for the receiver.
func (r *Receiver<T>) finalize() {
close(r.done)
}
// Package orderedmap provides an ordered map, implemented as a binary tree.
package orderedmap
import "chans"
// Map is an ordered map.
type Map<type K, V> struct {
root *node<K, V>
compare func(K, K) int
}
// node is the type of a node in the binary tree.
type node<type K, V> struct {
key K
val V
left, right *node<K, V>
}
// New returns a new map.
func New<type K, V>(compare func(K, K) int) *Map<K, V> {
return &Map{<K, V> compare: compare}
}
// find looks up key in the map, and returns either a pointer
// to the node holding key, or a pointer to the location where
// such a node would go.
func (m *Map<K, V>) find(key K) **node<K, V> {
pn := &m.root
for *pn != nil {
switch cmp := m.compare(key, (*pn).key); {
case cmp < 0:
pn = &(*pn).left
case cmp > 0:
pn = &(*pn).right
default:
return pn
}
}
return pn
}
// Insert inserts a new key/value into the map.
// If the key is already present, the value is replaced.
// Returns true if this is a new key, false if already present.
func (m *Map<K, V>) Insert(key K, val V) bool {
pn := m.find(key)
if *pn != nil {
(*pn).val = val
return false
}
*pn = &node{<K, V> key: key, val: val}
return true
}
// Find returns the value associated with a key, or zero if not present.
// The found result reports whether the key was found.
func (m *Map<K, V>) Find(key K) (V, bool) {
pn := m.find(key)
if *pn == nil {
var zero V // see the discussion of zero values, above
return zero, false
}
return (*pn).val, true
}
// keyValue is a pair of key and value used when iterating.
type keyValue<type K, V> struct {
key K
val V
}
// InOrder returns an iterator that does an in-order traversal of the map.
func (m *Map<K, V>) InOrder() *Iterator<K, V> {
sender, receiver := chans.Ranger(<keyValue<K, V>>)
var f func(*node<K, V>) bool
f = func(n *node<K, V>) bool {
if n == nil {
return true
}
// Stop sending values if sender.Send returns false,
// meaning that nothing is listening at the receiver end.
return f(n.left) &&
sender.Send(keyValue{<K, V> n.key, n.val}) &&
f(n.right)
}
go func() {
f(m.root)
sender.Close()
}()
return &Iterator{receiver}
}
// Iterator is used to iterate over the map.
type Iterator<type K, V> struct {
r *chans.Receiver(keyValue<K, V>)
}
// Next returns the next key and value pair, and a boolean indicating
// whether they are valid or whether we have reached the end.
func (it *Iterator<K, V>) Next() (K, V, bool) {
keyval, ok := it.r.Next()
if !ok {
var zerok K
var zerov V
return zerok, zerov, false
}
return keyval.key, keyval.val, true
}
import "container/orderedmap"
var m = orderedmap.New(<string, string> strings.Compare)
func Add(a, b string) {
m.Insert(a, b)
}
func Add(s, t []byte) []byte
package slices
// Append adds values to the end of a slice, returning a new slice.
func Append<type T>(s []T, t ...T) []T {
lens := len(s)
tot := lens + len(t)
if tot <= cap(s) {
s = s[:tot]
} else {
news := make([]T, tot, tot + tot/2)
copy(news, s)
s = news
}
copy(s[lens:tot], t)
return s
}
// Copy copies values from t to s, stopping when either slice is
// full, returning the number of values copied.
func Copy<type T>(s, t []T) int {
i := 0
for ; i < len(s) && i < len(t); i++ {
s[i] = t[i]
}
return i
}
s := slices.Append([]int{1, 2, 3}, 4, 5, 6)
slices.Copy(s[3:], []int{7, 8, 9})
package metrics
import "sync"
type Metric1<type T comparable> struct {
mu sync.Mutex
m map[T]int
}
func (m *Metric1<T>) Add(v T) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[T]int)
}
m[v]++
}
contract cmp2<T1, T2> {
comparable<T1>
comparable<T2>
}
type key2<type T1, T2 cmp2> struct {
f1 T1
f2 T2
}
type Metric2<type T1, T2 cmp2> struct {
mu sync.Mutex
m map[key2<T1, T2>]int
}
func (m *Metric2<T1, T2>) Add(v1 T1, v2 T2) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[key2<T1, T2>]int)
}
m[key{<T1, T2> v1, v2}]++
}
contract cmp3<T1, T2, T3> {
comparable<T1>
comparable<T2>
comparable<T3>
}
type key3<type T1, T2, T3 cmp3> struct {
f1 T1
f2 T2
f3 T3
}
type Metric3<type T1, T2, T3 cmp3> struct {
mu sync.Mutex
m map[key3<T1, T2, T3>]int
}
func (m *Metric3<T1, T2, T3>) Add(v1 T1, v2 T2, v3 T3) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[key3]int)
}
m[key{<T1, T2, T3> v1, v2, v3}]++
}
// Repeat for the maximum number of permitted arguments.
import "metrics"
var m = metrics.Metric2{<string, int>}
func F(s string, i int) {
m.Add(s, i) // this call is type checked at compile time
}
package list
// List is a linked list.
type List<type T> struct {
head, tail *element<T>
}
// An element is an entry in a linked list.
type element<type T> struct {
next *element<T>
val T
}
// Push pushes an element to the end of the list.
func (lst *List<T>) Push(v T) {
if lst.tail == nil {
lst.head = &element{<T> val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element{<T> val: v }
lst.tail = lst.tail.next
}
}
// Iterator ranges over a list.
type Iterator<type T> struct {
next **element<T>
}
// Range returns an Iterator starting at the head of the list.
func (lst *List<T>) Range() *Iterator<T> {
return Iterator{<T> next: &lst.head}
}
// Next advances the iterator.
// It returns whether there are more elements.
func (it *Iterator<T>) Next() bool {
if *it.next == nil {
return false
}
it.next = &(*it.next).next
return true
}
// Val returns the value of the current element.
// The bool result reports whether the value is valid.
func (it *Iterator<T>) Val() (T, bool) {
if *it.next == nil {
var zero T
return zero, false
}
return (*it.next).val, true
}
// Transform runs a transform function on a list returning a new list.
func Transform<type T1, T2>(lst *List<T1>, f func(T1) T2) *List<T2> {
ret := &List{<T2>}
it := lst.Range()
for {
if v, ok := it.Val(); ok {
ret.Push(f(v))
}
it.Next()
}
return ret
}
// Key is a key that can be used with Context.Value.
// Rather than calling Context.Value directly, use Key.Load.
//
// The zero value of Key is not ready for use; use NewKey.
type Key<type V> struct {
name string
}
// NewKey returns a key used to store values of type V in a Context.
// Every Key returned is unique, even if the name is reused.
func NewKey<type V>(name string) *Key {
return &Key{<V> name: name}
}
// WithValue returns a new context with v associated with k.
func (k *Key<V>) WithValue(parent Context, v V) Context {
return WithValue(parent, k, v)
}
// Value loads the value associated with k from ctx and reports
//whether it was successful.
func (k *Key<V>) Value(ctx Context) (V, bool) {
v, present := ctx.Value(k).(V)
return v.(V), present
}
// String returns the name and expected value type.
func (k *Key<V>) String() string {
var v V
return fmt.Sprintf("%s(%T)", k.name, v)
}
var ServerContextKey = context.NewKey(<*Server> "http_server")
// used as:
ctx := ServerContextKey.WithValue(ctx, srv)
s, present := ServerContextKey.Value(ctx)
// Numeric is a contract that matches any numeric type.
// It would likely be in a contracts package in the standard library.
contract Numeric<T> {
T int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64,
complex64, complex128
}
func DotProduct<type T Numeric>(s1, s2 []T) T {
if len(s1) != len(s2) {
panic("DotProduct: slices of unequal length")
}
var r T
for i := range s1 {
r += s1[i] * s2[i]
}
return r
}
// NumericAbs matches numeric types with an Abs method.
contract NumericAbs<T> {
Numeric<T>
T Abs() T
}
// AbsDifference computes the absolute value of the difference of
// a and b, where the absolute value is determined by the Abs method.
func AbsDifference<type T NumericAbs>(a, b T) T {
d := a - b
return d.Abs()
}
// OrderedNumeric matches numeric types that support the < operator.
contract OrderedNumeric<T> {
T int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64
}
// Complex matches the two complex types, which do not have a < operator.
contract Complex<T> {
T complex64, complex128
}
// OrderedAbs is a helper type that defines an Abs method for
// ordered numeric types.
type OrderedAbs<type T OrderedNumeric> T
func (a OrderedAbs<T>) Abs() OrderedAbs<T> {
if a < 0 {
return -a
}
return a
}
// ComplexAbs is a helper type that defines an Abs method for
// complex types.
type ComplexAbs<type T Complex> T
func (a ComplexAbs<T>) Abs() T {
r := float64(real(a))
i := float64(imag(a))
d := math.Sqrt(r * r + i * i)
return T(complex(d, 0))
}
func OrderedAbsDifference<type T OrderedNumeric>(a, b T) T {
return T(AbsDifference(OrderedAbs(<T> a), OrderedAbs(<T> b)))
}
func ComplexAbsDifference<type T Complex>(a, b T) T {
return T(AbsDifference(ComplexAbs(<T> a), ComplexAbs(<T> b)))
}
// This function is INVALID.
func GeneralAbsDifference<type T Numeric>(a, b T) T {
switch a.(type) {
case int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
float32, float64:
return OrderedAbsDifference(a, b) // INVALID
case complex64, complex128:
return ComplexAbsDifference(a, b) // INVALID
}
}