├── go.mod ├── nomono.fgg ├── nomono.go ├── figure3.go ├── figure6.go ├── atomic_value.go ├── atomic_nonvalue.go ├── figure5.go ├── figure5_mono.go ├── figure23.go ├── mapat.go ├── map.go ├── LICENSE ├── figure4.go ├── figure7_mono.go ├── figure7.go ├── assignable.md ├── append.go ├── unsafeslice ├── unsafeslice.go └── unsafeslice_test.go ├── append3.go ├── append1.go ├── containers └── containers.go ├── append2.go ├── ordered.go ├── iterate └── iterate.go ├── subtypes.md └── typelist.md /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bcmills/go2go 2 | 3 | go 1.21.1 4 | -------------------------------------------------------------------------------- /nomono.fgg: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // nomono is a non-monomorphizable program written in Featherweight Go. 6 | package main 7 | 8 | type Any interface{} 9 | type Recurser interface { 10 | Recurse() Any 11 | } 12 | type Impl(type a Any) struct{} 13 | func (this Impl(type a Recurser)) Recurse() Any { 14 | return Impl(Impl(a)){}.Recurse() 15 | } 16 | func main() { 17 | var _ Any = Impl(Recurser){}.Recurse() 18 | } 19 | -------------------------------------------------------------------------------- /nomono.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // nomono is a non-monomorphizable program under the Type Parameters draft 8 | // design. 9 | package main 10 | 11 | import "fmt" 12 | 13 | type Expander[a any] struct{ 14 | x *Expander[Expander[a]] 15 | } 16 | 17 | func (x Expander[a]) Expand() interface{} { 18 | return x.x 19 | } 20 | 21 | func main() { 22 | var a interface{} = Expander[struct{}]{}.x 23 | fmt.Sprintf("Expander[struct{}]{}.Expand(): %T\n", a) 24 | } 25 | -------------------------------------------------------------------------------- /figure3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | package main 8 | 9 | import "fmt" 10 | 11 | // Featherweight Go, Fig. 3 12 | 13 | type Function[a any, b any] interface { 14 | Apply(x a) b 15 | } 16 | type incr struct{ n int } 17 | func (this incr) Apply(x int) int { 18 | return x + this.n 19 | } 20 | type pos struct{} 21 | func (this pos) Apply(x int) bool { 22 | return x > 0 23 | } 24 | type compose[a any, b any, c any] struct { 25 | f Function[a, b] 26 | g Function[b, c] 27 | } 28 | func (this compose[a, b, c]) Apply(x a) c { 29 | return this.g.Apply(this.f.Apply(x)) 30 | } 31 | func main() { 32 | var f Function[int, bool] = compose[int, int, bool]{incr{-5}, pos{}} 33 | var b bool = f.Apply(3) // false 34 | 35 | fmt.Println(b) 36 | } 37 | -------------------------------------------------------------------------------- /figure6.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | package main 8 | 9 | import "fmt" 10 | 11 | // Fig. 6 12 | 13 | // Eval on Num 14 | type Evaler interface { 15 | Eval() int 16 | } 17 | type Num struct { 18 | value int 19 | } 20 | func (e Num) Eval() int { 21 | return e.value 22 | } 23 | // Eval on Plus 24 | type Plus[a Expr] struct { 25 | left a 26 | right a 27 | } 28 | func (e Plus[a]) Eval() int { 29 | return e.left.Eval() + e.right.Eval() 30 | } 31 | // String on Num 32 | type Stringer interface { 33 | String() string 34 | } 35 | func (e Num) String() string { 36 | return fmt.Sprintf("%d", e.value) 37 | } 38 | // String on Plus 39 | func (e Plus[a]) String() string { 40 | return fmt.Sprintf("%s+%s", e.left.String(), e.right.String()) 41 | } 42 | // tie it all together 43 | type Expr interface { 44 | Evaler 45 | Stringer 46 | } 47 | 48 | func main() { 49 | var e Expr = Plus[Expr]{Num{1}, Num{2}} 50 | var v int = e.Eval() // 3 51 | var s string = e.String() //"(1+2)" 52 | 53 | fmt.Println(v) 54 | fmt.Println(s) 55 | } 56 | -------------------------------------------------------------------------------- /atomic_value.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // atomic_value illustrates a type whose only requirement on its type parameter 8 | // is that the argument be a concrete (non-interface) type. 9 | // 10 | // This requirement cannot be expressed in the current design, leading to 11 | // the potential for run-time panics. 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "os" 17 | "sync/atomic" 18 | "syscall" 19 | ) 20 | 21 | // Value is a typed version of atomic.Value. 22 | // 23 | // But you'd better not instantiate it with an interface type, or you'll have to 24 | // be very careful to only ever store values of a consistent concrete type! 25 | type Value[T any] struct { 26 | a atomic.Value 27 | } 28 | 29 | func (v *Value[T]) Load() (x T) { 30 | return v.a.Load().(T) 31 | } 32 | 33 | func (v *Value[T]) Store(x T) { 34 | v.a.Store(x) 35 | } 36 | 37 | func main() { 38 | var err Value[error] 39 | err.Store(os.ErrNotExist) 40 | err.Store(syscall.ENOSYS) // Generics provide type-safety, right? 😉 41 | fmt.Printf("stored error: %v\n", err.Load()) 42 | } 43 | -------------------------------------------------------------------------------- /atomic_nonvalue.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // atomic_nonvalue illustrates a (less efficient) workaround for the atomic_value 8 | // example. 9 | // 10 | // It eliminates the type-safety hole by inducing an extra allocation that is 11 | // normally not needed. 12 | package main 13 | 14 | import ( 15 | "fmt" 16 | "os" 17 | "sync/atomic" 18 | "syscall" 19 | ) 20 | 21 | type Value[T any] struct { 22 | a atomic.Pointer[T] 23 | } 24 | 25 | func (v *Value[T]) Store(x T) { 26 | // Store a pointer to x instead of x itself, in case T is an interface type. 27 | // 28 | // It would be more efficient to require the caller to instantiate Value with 29 | // a non-interface type, so that the extra pointer allocation would be visible 30 | // on the caller side and could be avoided for non-interface T. 31 | v.a.Store(&x) 32 | } 33 | 34 | func (v *Value[T]) Load() (x T) { 35 | return *v.a.Load() 36 | } 37 | 38 | func main() { 39 | var err Value[error] 40 | err.Store(os.ErrNotExist) 41 | err.Store(syscall.ENOSYS) 42 | fmt.Printf("stored error: %v\n", err.Load()) 43 | } 44 | -------------------------------------------------------------------------------- /figure5.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // figure5 illustrates figure 5 of Featherweight Go. 8 | // 9 | // Unfortunately, it does not compile because the interface Eq 10 | // constrains its own type parameter. 11 | package main 12 | 13 | import "fmt" 14 | 15 | // Featherweight Go, Fig. 5 16 | 17 | // Eq is the Eq interface from figure 5. 18 | type Eq[a Eq[a]] interface { 19 | Equal(that a) bool 20 | } 21 | 22 | type Int int 23 | 24 | func (this Int) Equal(that Int) bool { 25 | return this == that 26 | } 27 | 28 | // Pair is the Pair struct from figure 5, but with the type constraints 29 | // strengthened to Eq because the final Go design for type parameters 30 | // does not allow methods that refine the receiver's type constraints. 31 | type Pair[a Eq, b Eq] struct { 32 | left a 33 | right b 34 | } 35 | 36 | func (this Pair[a, b]) Equal(that Pair[a, b]) bool { 37 | return this.left.Equal(that.left) && this.right.Equal(that.right) 38 | } 39 | 40 | func main() { 41 | var i, j Int = 1, 2 42 | var p Pair[Int, Int] = Pair[Int, Int]{i, j} 43 | fmt.Println(p.Equal(p)) // true 44 | } 45 | -------------------------------------------------------------------------------- /figure5_mono.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // figure5 illustrates figure 5 of Featherweight Go. 8 | // 9 | // Unfortunately, it does not compile because the interface Eq 10 | // constrains its own type parameter. 11 | package main 12 | 13 | import "fmt" 14 | 15 | // Featherweight Go, Fig. 5 16 | 17 | // Eq is the Eq interface from figure 5, but without type parameters. 18 | type Eq interface { 19 | Equal(that Eq) bool 20 | } 21 | 22 | type Int int 23 | 24 | func (this Int) Equal(that Eq) bool { 25 | return this == that 26 | } 27 | 28 | // Pair is the Pair struct from figure 5, but with the type constraints 29 | // strengthened to Eq because the final Go design for type parameters 30 | // does not allow methods that refine the receiver's type constraints. 31 | type Pair[a Eq, b Eq] struct { 32 | left a 33 | right b 34 | } 35 | 36 | func (this Pair[a, b]) Equal(that Pair[a, b]) bool { 37 | return this.left.Equal(that.left) && this.right.Equal(that.right) 38 | } 39 | 40 | func main() { 41 | var i, j Int = 1, 2 42 | var p Pair[Int, Int] = Pair[Int, Int]{i, j} 43 | fmt.Println(p.Equal(p)) // true 44 | } 45 | -------------------------------------------------------------------------------- /figure23.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | package main 8 | 9 | // Adapted from Featherweight Go, Fig. 23 10 | 11 | type Eq[a any] interface { 12 | Equal(that a) Bool 13 | } 14 | type Bool interface { 15 | Not() Bool 16 | Equal(that Bool) Bool 17 | cond(br Branches[any]) any 18 | } 19 | type Branches[a any] interface { 20 | IfTT() a 21 | IfFF() a 22 | } 23 | type Cond[a any] struct{} 24 | func (this Cond[a]) Eval(b Bool, br Branches[a]) a { 25 | return b.cond(anyBranches[a]{br}).(a) 26 | } 27 | type anyBranches[a any] struct { 28 | typed Branches[a] 29 | } 30 | func (br anyBranches[a]) IfTT() any { 31 | return br.typed.IfTT() 32 | } 33 | func (br anyBranches[a]) IfFF() any { 34 | return br.typed.IfFF() 35 | } 36 | 37 | type TT struct{} 38 | type FF struct{} 39 | 40 | func (this TT) Not() Bool { return FF{} } 41 | func (this FF) Not() Bool { return TT{} } 42 | 43 | func (this TT) Equal(that Bool) Bool { return that } 44 | func (this FF) Equal(that Bool) Bool { return that.Not() } 45 | 46 | func (this TT) cond(br Branches[any]) any { return br.IfTT() } 47 | func (this FF) cond(br Branches[any]) any { return br.IfFF() } 48 | 49 | func main() {} 50 | -------------------------------------------------------------------------------- /mapat.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // mapat illustrates the closest API to a conventional functional "Map" 8 | // that I could express under the Type Parameters draft design. 9 | package main 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | "syscall" 15 | ) 16 | 17 | // MapAt copies all of the elements in src to dst, using the supplied function 18 | // to convert between the two. 19 | func MapAt[T2, T1 any](dst []T2, src []T1, convert func(T1) T2) int { 20 | for i, x := range src { 21 | if i > len(dst) { 22 | return i 23 | } 24 | dst[i] = convert(x) 25 | } 26 | return len(src) 27 | } 28 | 29 | func main() { 30 | errnos := []syscall.Errno{syscall.ENOSYS, syscall.EINVAL} 31 | errs := make([]error, len(errnos)) 32 | 33 | MapAt(errs, errnos, func(err syscall.Errno) error { return err }) 34 | 35 | for _, err := range errs { 36 | fmt.Println(err) 37 | } 38 | 39 | cancelers := []context.CancelFunc{ 40 | func(){fmt.Println("cancel 1")}, 41 | func(){fmt.Println("cancel 2")}, 42 | } 43 | funcs := make([]func(), len(cancelers)) 44 | 45 | MapAt(funcs, cancelers, func(f context.CancelFunc) func() { return f }) 46 | 47 | for _, f := range funcs { 48 | f() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // map illustrates the awkwardness of functional APIs in the absence of 8 | // assignability and/or subtyping constraints. 9 | package main 10 | 11 | // Map returns a new slice containing the result of applying f to each element 12 | // in src. 13 | func Map[T1, T2 any](src []T1, f func(T1) T2) []T2 { 14 | dst := make([]T2, 0, len(src)) 15 | return AppendMapped(dst, src, f) 16 | } 17 | 18 | // AppendMapped applies f to each element in src, appending each result to dst 19 | // and returning the resulting slice. 20 | func AppendMapped[T1, T2 any](dst []T2, src []T1, f func(T1) T2) []T2 { 21 | for _, x := range src { 22 | dst = append(dst, f(x)) 23 | } 24 | return dst 25 | } 26 | 27 | func recv[T any](x <-chan T) T { 28 | return <-x 29 | } 30 | 31 | func main() { 32 | var chans []chan int 33 | 34 | // To map the recv function over a slice of bidirectional channels, 35 | // we need to wrap it: even though the element type "chan int" 36 | // is assignable to the argument type, the two function types are distinct. 37 | // 38 | // (There is no way for Map to declare an argument type “assignable from T1”, 39 | // so it must use “exactly T1” instead.) 40 | vals := Map(chans, func(x chan int) int { 41 | return recv(x) 42 | }) 43 | 44 | _ = vals 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /figure4.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // figure5 illustrates figure 4 of Featherweight Go. 8 | 9 | package main 10 | 11 | // Featherweight Go, Fig. 3 12 | 13 | type Function[a any, b any] interface { 14 | Apply(x a) b 15 | } 16 | type incr struct{ n int } 17 | func (this incr) Apply(x int) int { 18 | return x + this.n 19 | } 20 | type pos struct{} 21 | func (this pos) Apply(x int) bool { 22 | return x > 0 23 | } 24 | type compose[a any, b any, c any] struct { 25 | f Function[a, b] 26 | g Function[b, c] 27 | } 28 | func (this compose[a, b, c]) Apply(x a) c { 29 | return this.g.Apply(this.f.Apply(x)) 30 | } 31 | 32 | // Adapted from Featherweight Go, Fig. 4 33 | 34 | type Eq[a any] interface { 35 | Equal(a) bool 36 | } 37 | type Int int 38 | func (this Int) Equal(that Int) bool { 39 | return this == that 40 | } 41 | type List[a any] interface{ 42 | Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any 43 | } 44 | type Nil[a any] struct{} 45 | func (xs Nil[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { 46 | return casenil.Apply(xs) 47 | } 48 | type Cons[a any] struct { 49 | Head a 50 | Tail List[a] 51 | } 52 | func (xs Cons[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { 53 | return casecons.Apply(xs) 54 | } 55 | type lists[a any, b any] struct{} 56 | func (_ lists[a, b]) Map(f Function[a, b], xs List[a]) List[b] { 57 | return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(List[b]) 58 | } 59 | type mapNil[a any, b any] struct{} 60 | func (m mapNil[a, b]) Apply(_ Nil[a]) any { 61 | return Nil[b]{} 62 | } 63 | type mapCons[a any, b any] struct{ 64 | f Function[a, b] 65 | } 66 | func (m mapCons[a, b]) Apply(xs Cons[a]) any { 67 | return Cons[b]{m.f.Apply(xs.Head), lists[a, b]{}.Map(m.f, xs.Tail)} 68 | } 69 | 70 | func main() {} 71 | -------------------------------------------------------------------------------- /figure7_mono.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // figure7 illustrates figure 7 of Featherweight Go, 8 | // with the generic types defined in figure 7 converted to plain interfaces. 9 | package main 10 | 11 | // Featherweight Go, Fig. 3 12 | 13 | type Function[a any, b any] interface { 14 | Apply(x a) b 15 | } 16 | type incr struct{ n int } 17 | func (this incr) Apply(x int) int { 18 | return x + this.n 19 | } 20 | type pos struct{} 21 | func (this pos) Apply(x int) bool { 22 | return x > 0 23 | } 24 | type compose[a any, b any, c any] struct { 25 | f Function[a, b] 26 | g Function[b, c] 27 | } 28 | func (this compose[a, b, c]) Apply(x a) c { 29 | return this.g.Apply(this.f.Apply(x)) 30 | } 31 | 32 | // Adapted from Featherweight Go, Fig. 4 33 | 34 | type Eq[a any] interface { 35 | Equal(a) bool 36 | } 37 | type Int int 38 | func (this Int) Equal(that Int) bool { 39 | return this == that 40 | } 41 | type List[a any] interface{ 42 | Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any 43 | } 44 | type Nil[a any] struct{} 45 | func (xs Nil[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { 46 | return casenil.Apply(xs) 47 | } 48 | type Cons[a any] struct { 49 | Head a 50 | Tail List[a] 51 | } 52 | func (xs Cons[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { 53 | return casecons.Apply(xs) 54 | } 55 | type lists[a any, b any] struct{} 56 | func (_ lists[a, b]) Map(f Function[a, b], xs List[a]) List[b] { 57 | return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(List[b]) 58 | } 59 | type mapNil[a any, b any] struct{} 60 | func (m mapNil[a, b]) Apply(_ Nil[a]) any { 61 | return Nil[b]{} 62 | } 63 | type mapCons[a any, b any] struct{ 64 | f Function[a, b] 65 | } 66 | func (m mapCons[a, b]) Apply(xs Cons[a]) any { 67 | return Cons[b]{m.f.Apply(xs.Head), lists[a, b]{}.Map(m.f, xs.Tail)} 68 | } 69 | 70 | // Featherweight Go, Fig. 7 (monomorphized) 71 | 72 | type Edge interface { 73 | Source() Vertex 74 | Target() Vertex 75 | } 76 | type Vertex interface { 77 | Edges() List[Edge] 78 | } 79 | 80 | func main() {} 81 | -------------------------------------------------------------------------------- /figure7.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // figure7 illustrates figure 7 of Featherweight Go. 8 | // 9 | // Unfortunately, it does not compile: 10 | // 11 | // ./figure5.go:96:6: invalid recursive type: Edge refers to itself 12 | // ./figure5.go:96:6: invalid recursive type Edge 13 | // ./figure5.go:96:6: Edge refers to 14 | // ./figure5.go:100:6: Vertex refers to 15 | // ./figure5.go:96:6: Edge 16 | // ./figure5.go:100:6: invalid recursive type: Vertex refers to itself 17 | package main 18 | 19 | // Featherweight Go, Fig. 3 20 | 21 | type Function[a any, b any] interface { 22 | Apply(x a) b 23 | } 24 | type incr struct{ n int } 25 | 26 | func (this incr) Apply(x int) int { 27 | return x + this.n 28 | } 29 | 30 | type pos struct{} 31 | 32 | func (this pos) Apply(x int) bool { 33 | return x > 0 34 | } 35 | 36 | type compose[a any, b any, c any] struct { 37 | f Function[a, b] 38 | g Function[b, c] 39 | } 40 | 41 | func (this compose[a, b, c]) Apply(x a) c { 42 | return this.g.Apply(this.f.Apply(x)) 43 | } 44 | 45 | // Adapted from Featherweight Go, Fig. 4 46 | 47 | type Eq[a any] interface { 48 | Equal(a) bool 49 | } 50 | type Int int 51 | 52 | func (this Int) Equal(that Int) bool { 53 | return this == that 54 | } 55 | 56 | type List[a any] interface { 57 | Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any 58 | } 59 | type Nil[a any] struct{} 60 | 61 | func (xs Nil[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { 62 | return casenil.Apply(xs) 63 | } 64 | 65 | type Cons[a any] struct { 66 | Head a 67 | Tail List[a] 68 | } 69 | 70 | func (xs Cons[a]) Match(casenil Function[Nil[a], any], casecons Function[Cons[a], any]) any { 71 | return casecons.Apply(xs) 72 | } 73 | 74 | type lists[a any, b any] struct{} 75 | 76 | func (_ lists[a, b]) Map(f Function[a, b], xs List[a]) List[b] { 77 | return xs.Match(mapNil[a, b]{}, mapCons[a, b]{f}).(List[b]) 78 | } 79 | 80 | type mapNil[a any, b any] struct{} 81 | 82 | func (m mapNil[a, b]) Apply(_ Nil[a]) any { 83 | return Nil[b]{} 84 | } 85 | 86 | type mapCons[a any, b any] struct { 87 | f Function[a, b] 88 | } 89 | 90 | func (m mapCons[a, b]) Apply(xs Cons[a]) any { 91 | return Cons[b]{m.f.Apply(xs.Head), lists[a, b]{}.Map(m.f, xs.Tail)} 92 | } 93 | 94 | // Featherweight Go, Fig. 7 95 | 96 | type Edge[e Edge[e, v], v Vertex[e, v]] interface { 97 | Source() v 98 | Target() v 99 | } 100 | type Vertex[e Edge[e, v], v Vertex[e, v]] interface { 101 | Edges() List[e] 102 | } 103 | 104 | func main() {} 105 | -------------------------------------------------------------------------------- /assignable.md: -------------------------------------------------------------------------------- 1 | # Coherent interface constraints for assignability and convertibility? 2 | 3 | This document is a sketch of two interface types that could be added to the 4 | [draft Type Parameters design](http://golang.org/design/go2draft-type-parameters) 5 | to enable conversion and assignment in generic code. 6 | 7 | -------------------------------------------------------------------------------- 8 | 9 | The interface type `convertible.To(T)` is implemented by any dynamic type that 10 | can be converted to `T`, including `T` itself, any type assignable to `T`, and 11 | any type that has the same _[underlying type][]_ as `T`. A variable of type 12 | `convertible.To(T)` may be [converted][] to `T` itself. If the variable is the 13 | `nil` interface value, the result of the conversion is the zero-value of `T`. 14 | 15 | If `T` is itself an interface type, `convertible.To(T)` has the same method set 16 | as `T`. 17 | 18 | -------------------------------------------------------------------------------- 19 | 20 | The interface type `assignable.To(T)` is implemented by any dynamic type that is 21 | assignable to `T`. A variable of the interface type `assignable.To(T)` is 22 | assignable to `T` and, if the underlying type of `T` is a not a 23 | [defined type][], also to that underlying type. If the variable is `nil`, the 24 | value assigned is the zero-value of `T`. 25 | 26 | If `T` is itself an interface type, `assignable.To(T)` has the same method set 27 | as `T`. 28 | 29 | (If `T` is _not_ an interface type, a variable of type `assignable.To(T)` must 30 | not be assignable to any other defined type whose underlying type is `T` — even 31 | if `T` itself is not a defined type — because `assignable.To(T)` may store 32 | values of _other_ defined types.) 33 | 34 | For example, given: 35 | 36 | ```go 37 | type MyChan <-chan int 38 | type OtherChan <-chan int 39 | 40 | var ( 41 | a chan int 42 | b <-chan int 43 | c assignable.To(MyChan) 44 | d MyChan 45 | e OtherChan 46 | f assignable.To(<-chan int) 47 | g chan<- int 48 | ) 49 | 50 | ``` 51 | 52 | - `a` is assignable to `b`, `c`, `d`, `e`,`f`, and `g`. 53 | - `b` is assignable to `c`, `d`, `e`, and `f`. 54 | - `c` is assignable to `b`, `d`, and `f` (but not `e`, because `c` could 55 | contain a variable of type `MyChan` — which is not assignable to 56 | `OtherChan`). 57 | - `d` is assignable to `b`, `c`, and `f`. 58 | - `e` is assignable to `b` and `f`. 59 | - `f` is assignable to `b` (but not `d`, `d`, or `e`, because `f` could 60 | contain a variable of either type `OtherChan` or `MyChan`). 61 | - `g` is assignable to nothing. 62 | 63 | [defined type]: https://golang.org/ref/spec#Type_definitions 64 | [underlying type]: https://golang.org/ref/spec#Types 65 | [implements]: https://golang.org/ref/spec#Interface_types 66 | [converted]: https://golang.org/ref/spec#Conversions 67 | [literal]: https://golang.org/ref/spec#Types 68 | -------------------------------------------------------------------------------- /append.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // append illustrates the behavior of the "append" built-in in Go, for 8 | // comparison to possible semantics using the Type Parameters draft design. 9 | package main 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | ) 15 | 16 | type Funcs []func() 17 | type Cancels []context.CancelFunc 18 | type Recv <-chan int 19 | 20 | var ( 21 | f func() 22 | cancel context.CancelFunc 23 | funcSlice []func() 24 | cancelSlice []context.CancelFunc 25 | funcs Funcs 26 | cancels Cancels 27 | r <-chan int 28 | recvSlice []<-chan int 29 | R Recv 30 | RecvSlice []Recv 31 | b chan int 32 | bidiSlice []chan int 33 | ) 34 | 35 | func main() { 36 | ff := append(funcSlice, f) 37 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, f, ff) 38 | 39 | Ff := append(funcs, f) 40 | fmt.Printf("append(%T, %T) = %T\n", funcs, f, Ff) 41 | 42 | fc := append(funcSlice, cancel) 43 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, cancel, fc) 44 | 45 | cf := append(cancelSlice, f) 46 | fmt.Printf("append(%T, %T) = %T\n", cancelSlice, f, cf) 47 | 48 | Fc := append(funcs, cancel) 49 | fmt.Printf("append(%T, %T) = %T\n", funcs, cancel, Fc) 50 | 51 | Cc := append(cancels, f) 52 | fmt.Printf("append(%T, %T) = %T\n", cancels, f, Cc) 53 | 54 | ffc := append(funcSlice, f, cancel) 55 | fmt.Printf("append(%T, %T, %T) = %T\n", funcSlice, f, cancel, ffc) 56 | 57 | ff2 := append(funcSlice, funcSlice...) 58 | fmt.Printf("append(%T, %T...) = %T\n", funcSlice, funcSlice, ff2) 59 | 60 | FF2 := append(funcs, funcs...) 61 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcs, FF2) 62 | 63 | Ff2 := append(funcs, funcSlice...) 64 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcSlice, Ff2) 65 | 66 | // cannot use cancelSlice (type []context.CancelFunc) as type []func() in append 67 | // fc2 := append(funcSlice, cancelSlice...) 68 | // fmt.Printf("append(%T, %T...) = %T\n", funcSlice, cancelSlice, fc2) 69 | 70 | // cannot use cancels (type Cancels) as type []func() in append 71 | // FC2 := append(funcs, cancels...) 72 | // fmt.Printf("append(%T, %T...) = %T\n", funcs, cancels, FC2) 73 | 74 | rr := append(recvSlice, r) 75 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, r, rr) 76 | 77 | rb := append(recvSlice, b) 78 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rb) 79 | 80 | RR := append(RecvSlice, R) 81 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, R, RR) 82 | 83 | Rb := append(RecvSlice, b) 84 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, b, Rb) 85 | 86 | rrb := append(recvSlice, r, b) 87 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rrb) 88 | 89 | rr2 := append(recvSlice, recvSlice...) 90 | fmt.Printf("append(%T, %T...) = %T\n", recvSlice, recvSlice, rr2) 91 | 92 | // cannot use bidiSlice (type []chan int) as type []<-chan int in append 93 | // rb2 := append(recvSlice, bidiSlice...) 94 | // fmt.Printf("append(%T, %T...) = %T\n", recvSlice, bidiSlice, rb2) 95 | } 96 | -------------------------------------------------------------------------------- /unsafeslice/unsafeslice.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package unsafeslice implements common unsafe transformations involving slices, 6 | // based on the reflective prototype in github.com/bcmills/unsafeslice. 7 | // 8 | // unsafeslice uses the reflect package only for the SliceHeader type definition. 9 | // That dependency could be eliminated using a local definition and regression 10 | // test, as is done in the internal/unsafeheader package in Go 1.15. 11 | package unsafeslice 12 | 13 | import ( 14 | "fmt" 15 | "reflect" 16 | "unsafe" 17 | ) 18 | 19 | // Of returns a slice of length and capacity n located at p. 20 | // 21 | // The caller must ensure that p points to a backing array containing at least n 22 | // elements with an equivalent layout, size, and alignment to T. 23 | // 24 | // This implements one possible API for https://golang.org/issue/19367 25 | // and https://golang.org/issue/13656. 26 | func Of[T any](p *T, n int) []T { 27 | return unsafe.Slice(p, n) 28 | } 29 | 30 | // Convert returns a slice that refers to the same memory region as the slice src, 31 | // but at an arbitrary element type. 32 | // 33 | // At some call sites, ConvertAt may provide better type inference than Convert. 34 | // 35 | // The caller must ensure that the length and capacity in bytes of src are 36 | // integer multiples of the size of T2, and that the fields at each byte offset 37 | // in the resulting slices have equivalent layouts. 38 | // 39 | // This implements one possible API for https://golang.org/issue/38203. 40 | func Convert[T1, T2 any](src []T1) []T2 { 41 | srcElemSize := reflect.TypeOf(src).Elem().Size() 42 | capBytes := uintptr(cap(src)) * srcElemSize 43 | lenBytes := uintptr(len(src)) * srcElemSize 44 | 45 | dstElemSize := reflect.TypeOf((*T2)(nil)).Elem().Size() 46 | 47 | if capBytes%dstElemSize != 0 { 48 | panic(fmt.Sprintf("Convert: src capacity (%d bytes) is not a multiple of dst element size (%T: %d bytes)", capBytes, *new(T2), dstElemSize)) 49 | } 50 | dstCap := capBytes / dstElemSize 51 | if int(dstCap) < 0 || uintptr(int(dstCap)) != dstCap { 52 | panic(fmt.Sprintf("Convert: dst capacity (%d) overflows int", dstCap)) 53 | } 54 | 55 | if lenBytes%dstElemSize != 0 { 56 | panic(fmt.Sprintf("Convert: src length (%d bytes) is not a multiple of dst element size (%T: %d bytes)", lenBytes, *new(T2), dstElemSize)) 57 | } 58 | dstLen := lenBytes / dstElemSize 59 | if int(dstLen) < 0 || uintptr(int(dstLen)) != dstLen { 60 | panic(fmt.Sprintf("ConvertAt: dst length (%d) overflows int", dstLen)) 61 | } 62 | 63 | return unsafe.Slice((*T2)(unsafe.Pointer(unsafe.SliceData(src))), dstCap)[:dstLen] 64 | } 65 | 66 | // ConvertAt sets dst, which must be non-nil, to a slice that refers to the same 67 | // memory region as the slice src, but possibly at a different type. 68 | // 69 | // The caller must ensure that the length and capacity in bytes of src are 70 | // integer multiples of the size of T2, and that the fields at each byte offset 71 | // in the resulting slices have equivalent layouts. 72 | // 73 | // This implements one possible API for https://golang.org/issue/38203. 74 | func ConvertAt[T2, T1 any](dst *[]T2, src []T1) { 75 | *dst = Convert[T1, T2](src) 76 | } 77 | 78 | // AsPointer returns a pointer to the array backing src[0:len(src)] as type *T. 79 | // 80 | // The caller must ensure that the length in bytes of src is an integer multiple 81 | // of the size of T, and that the fields at each byte offset in the resulting 82 | // slice have a layout equivalent to T. 83 | // 84 | // At some call sites, SetPointer may provide better type inference than 85 | // AsPointer. 86 | func AsPointer[E, T any](src []E) *T { 87 | return unsafe.SliceData(Convert[E, T](src[:len(src):len(src)])) 88 | } 89 | 90 | // SetPointer sets dst, which must be non-nil, to a pointer that refers to the 91 | // elements of src. Typically, dst should point to a pointer to an array with 92 | // the same length and element type as src. 93 | func SetPointer[T, E any](dst **T, src []E) { 94 | *dst = AsPointer[E, T](src) 95 | } 96 | -------------------------------------------------------------------------------- /append3.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // append3 illustrates the properties of a 3-type-parameter "append" variant. 8 | // 9 | // With a more advanced type-inference algorithm and a proper "assignable to" 10 | // constraint, it could support inference for all of the same cases as the 11 | // built-in "append" does today, plus a few others. 12 | package main 13 | 14 | import ( 15 | "context" 16 | "fmt" 17 | "reflect" 18 | ) 19 | 20 | type sliceOf[E any] interface{ ~[]E } 21 | 22 | // buggyAssignableTo simulates an “assignable to” type constraint using 23 | // something a little too permissive ("any"). 24 | // We confirm assignability at run-time using the reflect package. 25 | type buggyAssignableTo[T any] interface { any } 26 | 27 | func append[T any, T2 buggyAssignableTo[T], S sliceOf[T]](s S, t ...T2) S { 28 | // Confirm that T2 is assignable to T. 29 | // Ideally this should happen in the type system instead of at run-time. 30 | rt := reflect.TypeOf(s).Elem() 31 | rt2 := reflect.TypeOf(t).Elem() 32 | if !rt2.AssignableTo(rt) { 33 | panic("append: T2 is not assignable to T") 34 | } 35 | 36 | lens := len(s) 37 | tot := lens + len(t) 38 | if tot < 0 { 39 | panic("append: cap out of range") 40 | } 41 | if tot > cap(s) { 42 | news := make([]T, tot, tot+tot/2) 43 | copy(news, s) 44 | s = news 45 | } 46 | 47 | s = s[:tot] 48 | for i, x := range t { 49 | // We need to bounce through reflect because buggyAssignableTo doesn't 50 | // actually enable assignment. 51 | xt := reflect.ValueOf(x).Convert(rt).Interface().(T) 52 | s[lens+i] = xt 53 | } 54 | return s 55 | } 56 | 57 | type Funcs []func() 58 | type Cancels []context.CancelFunc 59 | type Recv <-chan int 60 | 61 | var ( 62 | f func() 63 | cancel context.CancelFunc 64 | funcSlice []func() 65 | cancelSlice []context.CancelFunc 66 | funcs Funcs 67 | cancels Cancels 68 | r <-chan int 69 | recvSlice []<-chan int 70 | R Recv 71 | RecvSlice []Recv 72 | b chan int 73 | bidiSlice []chan int 74 | ) 75 | 76 | func main() { 77 | ff := append(funcSlice, f) 78 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, f, ff) 79 | 80 | Ff := append(funcs, f) 81 | fmt.Printf("append(%T, %T) = %T\n", funcs, f, Ff) 82 | 83 | fc := append(funcSlice, cancel) 84 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, cancel, fc) 85 | 86 | cf := append(cancelSlice, f) 87 | fmt.Printf("append(%T, %T) = %T\n", cancelSlice, f, cf) 88 | 89 | Fc := append(funcs, cancel) 90 | fmt.Printf("append(%T, %T) = %T\n", funcs, cancel, Fc) 91 | 92 | Cc := append(cancels, f) 93 | fmt.Printf("append(%T, %T) = %T\n", cancels, f, Cc) 94 | 95 | ffc := append(funcSlice, f, cancel) 96 | fmt.Printf("append(%T, %T, %T) = %T\n", funcSlice, f, cancel, ffc) 97 | 98 | ff2 := append(funcSlice, funcSlice...) 99 | fmt.Printf("append(%T, %T...) = %T\n", funcSlice, funcSlice, ff2) 100 | 101 | FF2 := append(funcs, funcs...) 102 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcs, FF2) 103 | 104 | Ff2 := append(funcs, funcSlice...) 105 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcSlice, Ff2) 106 | 107 | fc2 := append(funcSlice, cancelSlice...) 108 | fmt.Printf("append(%T, %T...) = %T\n", funcSlice, cancelSlice, fc2) 109 | 110 | FC2 := append(funcs, cancels...) 111 | fmt.Printf("append(%T, %T...) = %T\n", funcs, cancels, FC2) 112 | 113 | rr := append(recvSlice, r) 114 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, r, rr) 115 | 116 | rb := append(recvSlice, b) 117 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rb) 118 | 119 | RR := append(RecvSlice, R) 120 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, R, RR) 121 | 122 | Rb := append(RecvSlice, b) 123 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, b, Rb) 124 | 125 | rrb := append(recvSlice, r, b) 126 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rrb) 127 | 128 | rr2 := append(recvSlice, recvSlice...) 129 | fmt.Printf("append(%T, %T...) = %T\n", recvSlice, recvSlice, rr2) 130 | 131 | rb2 := append(recvSlice, bidiSlice...) 132 | fmt.Printf("append(%T, %T...) = %T\n", recvSlice, bidiSlice, rb2) 133 | } 134 | -------------------------------------------------------------------------------- /append1.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // append1 illustrates the properties of the "append" variation described in the 8 | // Type Parameters draft design. 9 | package main 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | ) 15 | 16 | func append[T any](s []T, t ...T) []T { 17 | lens := len(s) 18 | tot := lens + len(t) 19 | if tot < 0 { 20 | panic("append: cap out of range") 21 | } 22 | if tot > cap(s) { 23 | news := make([]T, tot, tot+tot/2) 24 | copy(news, s) 25 | s = news 26 | } 27 | s = s[:tot] 28 | copy(s[lens:], t) 29 | return s 30 | } 31 | 32 | type Funcs []func() 33 | type Cancels []context.CancelFunc 34 | type Recv <-chan int 35 | 36 | var ( 37 | f func() 38 | cancel context.CancelFunc 39 | funcSlice []func() 40 | cancelSlice []context.CancelFunc 41 | funcs Funcs 42 | cancels Cancels 43 | r <-chan int 44 | recvSlice []<-chan int 45 | R Recv 46 | RecvSlice []Recv 47 | b chan int 48 | bidiSlice []chan int 49 | ) 50 | 51 | func main() { 52 | ff := append(funcSlice, f) 53 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, f, ff) 54 | 55 | // returns type []func() instead of Funcs 56 | Ff := append(funcs, f) 57 | fmt.Printf("append(%T, %T) = %T\n", funcs, f, Ff) 58 | 59 | // cannot use funcSlice (variable of type []func()) as []context.CancelFunc value in argument to append 60 | fc := append[func()](funcSlice, cancel) 61 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, cancel, fc) 62 | 63 | cf := append(cancelSlice, f) 64 | fmt.Printf("append(%T, %T) = %T\n", cancelSlice, f, cf) 65 | 66 | // returns type []func instead of Funcs 67 | // cannot use funcs (variable of type Funcs) as []context.CancelFunc value in argument to append 68 | Fc := append[func()](funcs, cancel) 69 | fmt.Printf("append(%T, %T) = %T\n", funcs, cancel, Fc) 70 | 71 | Cc := append(cancels, f) 72 | fmt.Printf("append(%T, %T) = %T\n", cancels, f, Cc) 73 | 74 | // cannot use funcSlice (variable of type []func()) as []context.CancelFunc value in argument to append 75 | ffc := append[func()](funcSlice, f, cancel) 76 | fmt.Printf("append(%T, %T, %T) = %T\n", funcSlice, f, cancel, ffc) 77 | 78 | ff2 := append(funcSlice, funcSlice...) 79 | fmt.Printf("append(%T, %T...) = %T\n", funcSlice, funcSlice, ff2) 80 | 81 | // returns type []func() instead of Funcs 82 | FF2 := append(funcs, funcs...) 83 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcs, FF2) 84 | 85 | // returns type []func() instead of Funcs 86 | Ff2 := append(funcs, funcSlice...) 87 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcSlice, Ff2) 88 | 89 | // type []context.CancelFunc of cancelSlice does not match inferred type []func() for []T 90 | // cannot use cancelSlice (variable of type []context.CancelFunc) as []func() value in argument to append[func()] 91 | // fc2 := append[func()](funcSlice, cancelSlice...) 92 | // fmt.Printf("append(%T, %T...) = %T\n", funcSlice, cancelSlice, fc2) 93 | 94 | // type Cancels of cancels does not match inferred type []func() for []T 95 | // cannot use cancels (variable of type Cancels) as []func() value in argument to append[func()] 96 | // FC2 := append[func()](funcs, cancels...) 97 | // fmt.Printf("append(%T, %T...) = %T\n", funcs, cancels, FC2) 98 | 99 | rr := append(recvSlice, r) 100 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, r, rr) 101 | 102 | rb := append(recvSlice, b) 103 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rb) 104 | 105 | RR := append(RecvSlice, R) 106 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, R, RR) 107 | 108 | Rb := append(RecvSlice, b) 109 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, b, Rb) 110 | 111 | rrb := append(recvSlice, r, b) 112 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rrb) 113 | 114 | rr2 := append(recvSlice, recvSlice...) 115 | fmt.Printf("append(%T, %T...) = %T\n", recvSlice, recvSlice, rr2) 116 | 117 | // type []chan int of bidiSlice does not match inferred type []<-chan int for []T 118 | // cannot use bidiSlice (variable of type []chan int) as []<-chan int value in argument to append[<-chan int] 119 | // rb2 := append[<-chan int](recvSlice, bidiSlice...) 120 | // fmt.Printf("append(%T, %T...) = %T\n", recvSlice, bidiSlice, rb2) 121 | } 122 | -------------------------------------------------------------------------------- /containers/containers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package containers defines generic interfaces for built-in container types. 6 | package containers 7 | 8 | type Lenner interface { 9 | Len() int 10 | } 11 | 12 | type Capper interface { 13 | Cap() int 14 | } 15 | 16 | type Indexer[K, V any] interface { 17 | Index(K) (V, bool) 18 | } 19 | 20 | type IndexSetter[K, V any] interface { 21 | Index(K) (V, bool) 22 | SetIndex(K, V) 23 | } 24 | 25 | type Sender[V any] interface { 26 | Send(V) 27 | } 28 | 29 | type Closer interface { 30 | Close() 31 | } 32 | 33 | type Receiver[V any] interface { 34 | Receive() (V, bool) 35 | } 36 | 37 | type KeyRanger[K any] interface { 38 | RangeKeys(func(K) (ok bool)) 39 | } 40 | 41 | type ElemRanger[V any] interface { 42 | RangeElems(func(V) (ok bool)) 43 | } 44 | 45 | type Ranger[K, V any] interface { 46 | RangeKeys(func(K) (ok bool)) 47 | RangeElems(func(V) (ok bool)) 48 | Range(func(K, V) (ok bool)) 49 | } 50 | 51 | type String string 52 | 53 | func (s String) Len() int { return len(s) } 54 | 55 | func (s String) Index(i int) (byte, bool) { 56 | if i < 0 || i >= len(s) { 57 | return 0, false 58 | } 59 | return s[i], true 60 | } 61 | 62 | func (s String) RangeKeys(f func(i int) bool) { 63 | for i := range s { 64 | if !f(i) { 65 | break 66 | } 67 | } 68 | } 69 | 70 | func (s String) RangeElems(f func(r rune) bool) { 71 | for _, r := range s { 72 | if !f(r) { 73 | break 74 | } 75 | } 76 | } 77 | 78 | func (s String) Range(f func(i int, r rune) bool) { 79 | for i, r := range s { 80 | if !f(i, r) { 81 | break 82 | } 83 | } 84 | } 85 | 86 | type Slice[T any] []T 87 | 88 | func (s Slice[T]) Len() int { return len(s) } 89 | func (s Slice[T]) Cap() int { return cap(s) } 90 | func (s Slice[T]) SetIndex(i int, x T) { s[i] = x } 91 | 92 | func (s Slice[T]) Index(i int) (T, bool) { 93 | if i < 0 || i >= len(s) { 94 | return *new(T), false 95 | } 96 | return s[i], true 97 | } 98 | 99 | func (s Slice[T]) RangeKeys(f func(i int) bool) { 100 | for i := range s { 101 | if !f(i) { 102 | break 103 | } 104 | } 105 | } 106 | 107 | func (s Slice[T]) RangeElems(f func(x T) bool) { 108 | for _, x := range s { 109 | if !f(x) { 110 | break 111 | } 112 | } 113 | } 114 | 115 | func (s Slice[T]) Range(f func(i int, x T) bool) { 116 | for i, x := range s { 117 | if !f(i, x) { 118 | break 119 | } 120 | } 121 | } 122 | 123 | type Map[K comparable, V any] map[K]V 124 | 125 | func (m Map[K, V]) Len() int { return len(m) } 126 | func (m Map[K, V]) SetIndex(k K, v V) { m[k] = v } 127 | 128 | func (m Map[K, V]) Index(k K) (V, bool) { 129 | v, ok := m[k] 130 | return v, ok 131 | } 132 | 133 | func (m Map[K, V]) RangeKeys(f func(K) bool) { 134 | for k := range m { 135 | if !f(k) { 136 | break 137 | } 138 | } 139 | } 140 | 141 | func (m Map[K, V]) RangeElems(f func(V) bool) { 142 | for _, v := range m { 143 | if !f(v) { 144 | break 145 | } 146 | } 147 | } 148 | 149 | func (m Map[K, V]) Range(f func(K, V) bool) { 150 | for k, v := range m { 151 | if !f(k, v) { 152 | break 153 | } 154 | } 155 | } 156 | 157 | 158 | 159 | type Chan[T any] chan T 160 | 161 | func (c Chan[T]) Len() int { return len(c) } 162 | func (c Chan[T]) Cap() int { return cap(c) } 163 | func (c Chan[T]) Send(x T) { c <- x } 164 | func (c Chan[T]) Close() { close(c) } 165 | 166 | func (c Chan[T]) Recv() (T, bool) { 167 | x, ok := <-c 168 | return x, ok 169 | } 170 | 171 | func (c Chan[T]) RangeElems(f func(T) bool) { 172 | for x := range c { 173 | if !f(x) { 174 | break 175 | } 176 | } 177 | } 178 | 179 | type RecvChan[T any] <-chan T 180 | 181 | func (c RecvChan[T]) Len() int { return len(c) } 182 | func (c RecvChan[T]) Cap() int { return cap(c) } 183 | 184 | func (c RecvChan[T]) Recv() (T, bool) { 185 | x, ok := <-c 186 | return x, ok 187 | } 188 | 189 | func (c RecvChan[T]) RangeElems(f func(x T) bool) { 190 | for x := range c { 191 | if !f(x) { 192 | break 193 | } 194 | } 195 | } 196 | 197 | type SendChan[T any] chan<- T 198 | 199 | func (c SendChan[T]) Len() int { return len(c) } 200 | func (c SendChan[T]) Cap() int { return cap(c) } 201 | func (c SendChan[T]) Send(x T) { c <- x } 202 | func (c SendChan[T]) Close() { close(c) } 203 | -------------------------------------------------------------------------------- /append2.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | 7 | // append2 illustrates the properties of a two-type-parameter "append" variant. 8 | // With explicit type annotations, it seems to be able to handle the same cases 9 | // as the existing built-in append. 10 | // 11 | // However, I don't see a plausible type-inference algorithm that makes it work 12 | // for all of those cases without explicit type annotations. 13 | package main 14 | 15 | import ( 16 | "context" 17 | "fmt" 18 | ) 19 | 20 | type sliceOf[E any] interface{ ~[]E } 21 | 22 | func append[T any, S sliceOf[T]](s S, t ...T) S { 23 | lens := len(s) 24 | tot := lens + len(t) 25 | if tot < 0 { 26 | panic("append: cap out of range") 27 | } 28 | if tot > cap(s) { 29 | news := make([]T, tot, tot+tot/2) 30 | copy(news, s) 31 | s = news 32 | } 33 | s = s[:tot] 34 | copy(s[lens:], t) 35 | return s 36 | } 37 | 38 | type Funcs []func() 39 | type Cancels []context.CancelFunc 40 | type Recv <-chan int 41 | 42 | var ( 43 | f func() 44 | cancel context.CancelFunc 45 | funcSlice []func() 46 | cancelSlice []context.CancelFunc 47 | funcs Funcs 48 | cancels Cancels 49 | r <-chan int 50 | recvSlice []<-chan int 51 | R Recv 52 | RecvSlice []Recv 53 | b chan int 54 | bidiSlice []chan int 55 | ) 56 | 57 | func main() { 58 | ff := append(funcSlice, f) 59 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, f, ff) 60 | 61 | Ff := append(funcs, f) 62 | fmt.Printf("append(%T, %T) = %T\n", funcs, f, Ff) 63 | 64 | // []func() does not satisfy sliceOf[context.CancelFunc] ([]func() missing in ~[]context.CancelFunc) 65 | fc := append[func()](funcSlice, cancel) 66 | fmt.Printf("append(%T, %T) = %T\n", funcSlice, cancel, fc) 67 | 68 | cf := append(cancelSlice, f) 69 | fmt.Printf("append(%T, %T) = %T\n", cancelSlice, f, cf) 70 | 71 | // Funcs does not satisfy sliceOf[context.CancelFunc] (Funcs missing in ~[]context.CancelFunc) 72 | Fc := append[func()](funcs, cancel) 73 | fmt.Printf("append(%T, %T) = %T\n", funcs, cancel, Fc) 74 | 75 | Cc := append(cancels, f) 76 | fmt.Printf("append(%T, %T) = %T\n", cancels, f, Cc) 77 | 78 | // []func() does not satisfy sliceOf[context.CancelFunc] ([]func() missing in ~[]context.CancelFunc) 79 | ffc := append[func()](funcSlice, f, cancel) 80 | fmt.Printf("append(%T, %T, %T) = %T\n", funcSlice, f, cancel, ffc) 81 | 82 | ff2 := append(funcSlice, funcSlice...) 83 | fmt.Printf("append(%T, %T...) = %T\n", funcSlice, funcSlice, ff2) 84 | 85 | FF2 := append(funcs, funcs...) 86 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcs, FF2) 87 | 88 | Ff2 := append(funcs, funcSlice...) 89 | fmt.Printf("append(%T, %T...) = %T\n", funcs, funcSlice, Ff2) 90 | 91 | // []func() does not satisfy sliceOf[context.CancelFunc] ([]func() missing in ~[]context.CancelFunc) 92 | // cannot use cancelSlice (variable of type []context.CancelFunc) as []func() value in argument to append[func()] 93 | // fc2 := append[func()](funcSlice, cancelSlice...) 94 | // fmt.Printf("append(%T, %T...) = %T\n", funcSlice, cancelSlice, fc2) 95 | 96 | // Funcs does not satisfy sliceOf[context.CancelFunc] (Funcs missing in ~[]context.CancelFunc) 97 | // cannot use cancels (variable of type Cancels) as []func() value in argument to append[func()] 98 | // FC2 := append[func()](funcs, cancels...) 99 | // fmt.Printf("append(%T, %T...) = %T\n", funcs, cancels, FC2) 100 | 101 | rr := append(recvSlice, r) 102 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, r, rr) 103 | 104 | rb := append(recvSlice, b) 105 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rb) 106 | 107 | RR := append(RecvSlice, R) 108 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, R, RR) 109 | 110 | Rb := append(RecvSlice, b) 111 | fmt.Printf("append(%T, %T) = %T\n", RecvSlice, b, Rb) 112 | 113 | rrb := append(recvSlice, r, b) 114 | fmt.Printf("append(%T, %T) = %T\n", recvSlice, b, rrb) 115 | 116 | rr2 := append(recvSlice, recvSlice...) 117 | fmt.Printf("append(%T, %T...) = %T\n", recvSlice, recvSlice, rr2) 118 | 119 | // cannot use bidiSlice (variable of type []chan int) as []<-chan int value in argument to append 120 | // rb2 := append(recvSlice, bidiSlice...) 121 | // fmt.Printf("append(%T, %T...) = %T\n", recvSlice, bidiSlice, rb2) 122 | } 123 | -------------------------------------------------------------------------------- /unsafeslice/unsafeslice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package unsafeslice_test 6 | 7 | import ( 8 | "fmt" 9 | "testing" 10 | 11 | "github.com/bcmills/go2go/unsafeslice" 12 | ) 13 | 14 | // asCPointer returns b as a C-style pointer and length 15 | func asCPointer(b []byte) (*byte, int) { 16 | if len(b) == 0 { 17 | return nil, 0 18 | } 19 | return &b[0], len(b) 20 | } 21 | 22 | func ExampleOf() { 23 | original := []byte("Hello, world!") 24 | p, n := asCPointer(original) 25 | 26 | alias := unsafeslice.Of(p, n) 27 | 28 | fmt.Printf("original: %s\n", original) 29 | fmt.Printf("alias: %s\n", alias) 30 | copy(alias, "Adios") 31 | fmt.Printf("original: %s\n", original) 32 | fmt.Printf("alias: %s\n", alias) 33 | 34 | // Output: 35 | // original: Hello, world! 36 | // alias: Hello, world! 37 | // original: Adios, world! 38 | // alias: Adios, world! 39 | } 40 | 41 | func ExampleConvert() { 42 | // For this example, we're going to do a transformation on some ASCII text. 43 | // That transformation is not endian-sensitive, so we can reinterpret the text 44 | // as a slice of uint32s to process it word-at-a-time instead of 45 | // byte-at-a-time. 46 | 47 | const input = "HELLO, WORLD!" 48 | 49 | // Allocate an aligned backing buffer. 50 | buf := make([]uint32, (len(input)+3)/4) 51 | 52 | // Reinterpret it as a byte slice so that we can copy in our text. 53 | // The call site here is awkward because we have to specify both types, 54 | // even though the source type can be inferred. 55 | alias := unsafeslice.Convert[uint32, byte](buf) 56 | copy(alias, input) 57 | 58 | // Perform an endian-insensitive transformation word-by-word instead of 59 | // byte-by-byte. 60 | for i := range buf { 61 | buf[i] |= 0x20202020 62 | } 63 | 64 | // Read the result back out of the byte-slice view to interpret it as text. 65 | fmt.Printf("%s\n", alias[:len(input)]) 66 | 67 | // Output: 68 | // hello, world! 69 | } 70 | 71 | func ExampleConvertAt() { 72 | // For this example, we're going to do a transformation on some ASCII text. 73 | // That transformation is not endian-sensitive, so we can reinterpret the text 74 | // as a slice of uint32s to process it word-at-a-time instead of 75 | // byte-at-a-time. 76 | 77 | const input = "HELLO, WORLD!" 78 | 79 | // Allocate an aligned backing buffer. 80 | buf := make([]uint32, (len(input)+3)/4) 81 | 82 | // Reinterpret it as a byte slice so that we can copy in our text. 83 | var alias []byte 84 | unsafeslice.ConvertAt(&alias, buf) 85 | copy(alias, input) 86 | 87 | // Perform an endian-insensitive transformation word-by-word instead of 88 | // byte-by-byte. 89 | for i := range buf { 90 | buf[i] |= 0x20202020 91 | } 92 | 93 | // Read the result back out of the byte-slice view to interpret it as text. 94 | fmt.Printf("%s\n", alias[:len(input)]) 95 | 96 | // Output: 97 | // hello, world! 98 | } 99 | 100 | type big [1 << 20]byte 101 | 102 | func TestOfWithVeryLargeTypeDoesNotPanic(t *testing.T) { 103 | var x big 104 | _ = unsafeslice.Of(&x, 1) 105 | } 106 | 107 | func TestConvertAt(t *testing.T) { 108 | u32 := []uint32{0x00102030, 0x40506070}[:1] 109 | var b []byte 110 | unsafeslice.ConvertAt(&b, u32) 111 | 112 | if want := len(u32) * 4; len(b) != want { 113 | t.Errorf("ConvertAt(_, %x): length = %v; want %v", u32, len(b), want) 114 | } 115 | if want := cap(u32) * 4; cap(b) != want { 116 | t.Errorf("ConvertAt(_, %x): capacity = %v; want %v", u32, cap(b), want) 117 | } 118 | } 119 | 120 | func TestConvertAtErrors(t *testing.T) { 121 | cases := []struct { 122 | desc string 123 | dst *[]uint32 124 | src []byte 125 | }{ 126 | { 127 | desc: "incompatible capacity", 128 | src: []byte("foobar")[:4:6], 129 | dst: new([]uint32), 130 | }, 131 | { 132 | desc: "incompatible length", 133 | src: []byte("foobar\x00\x00")[:6], 134 | dst: new([]uint32), 135 | }, 136 | } 137 | for _, tc := range cases { 138 | t.Run(tc.desc, func(t *testing.T) { 139 | defer func() { 140 | if msg := recover(); msg != nil { 141 | t.Logf("recovered: %v", msg) 142 | } else { 143 | t.Errorf("ConvertAt failed to panic as expected.") 144 | } 145 | }() 146 | 147 | unsafeslice.ConvertAt(tc.dst, tc.src) 148 | }) 149 | } 150 | } 151 | 152 | type sliceOfBig []big 153 | 154 | func TestConvertAtTypeInference(t *testing.T) { 155 | var b []byte 156 | var s sliceOfBig 157 | 158 | // This call requires an explicit type conversion. Otherwise, it fails with the error: 159 | // cannot use &s (value of type *sliceOfBig) as *[]big value in argument 160 | unsafeslice.ConvertAt((*[]big)(&s), b) 161 | } 162 | -------------------------------------------------------------------------------- /ordered.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // ordered illustrates a workaround for the GeneralAbsDifference example in the 6 | // Type Parameters draft, and the limitations of that workaround. 7 | // 8 | // Unfortunately, that workaround itself no longer works due to 9 | // bugs and feature reductions in the implementation of generics 10 | // that occurred after the proposal process. 11 | package main 12 | 13 | import ( 14 | "fmt" 15 | "math" 16 | ) 17 | 18 | // Numeric is a constraint that matches any numeric type. 19 | // It would likely be in a constraints package in the standard library. 20 | type Numeric interface { 21 | ~int | ~int8 | ~int16 | ~int32 | ~int64 | 22 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | 23 | ~float32 | ~float64 | 24 | ~complex64 | ~complex128 25 | } 26 | 27 | // NumericAbs matches numeric types with an Abs method. 28 | type NumericAbs[T any] interface { 29 | Numeric 30 | Abs() T 31 | } 32 | 33 | // AbsDifference computes the absolute value of the difference of 34 | // a and b, where the absolute value is determined by the Abs method. 35 | func AbsDifference[T NumericAbs[T]](a, b T) T { 36 | d := a - b 37 | return d.Abs() 38 | } 39 | 40 | // OrderedNumeric matches numeric types that support the < operator. 41 | type OrderedNumeric interface { 42 | ~int | ~int8 | ~int16 | ~int32 | ~int64 | 43 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | 44 | ~float32 | ~float64 45 | } 46 | 47 | // Complex matches the two complex types, which do not have a < operator. 48 | type Complex interface { 49 | ~complex64 | ~complex128 50 | } 51 | 52 | // OrderedAbs is a helper type that defines an Abs method for 53 | // ordered numeric types. 54 | type OrderedAbs[T OrderedNumeric] T 55 | 56 | func (a OrderedAbs[T]) Abs() OrderedAbs[T] { 57 | if a.V < 0 { 58 | return OrderedAbs[T](-a) 59 | } 60 | return OrderedAbs[T](a) 61 | } 62 | 63 | // ComplexAbs is a helper type that defines an Abs method for 64 | // complex types. 65 | type ComplexAbs[T Complex] T 66 | 67 | func (a ComplexAbs[T]) Abs() ComplexAbs[T] { 68 | d := math.Hypot(float64(real(a)), float64(imag(a))) 69 | return ComplexAbs[T]{T(complex(d, 0))} 70 | } 71 | 72 | // OrderedAbsDifference returns the absolute value of the difference 73 | // between a and b, where a and b are of an ordered type. 74 | func OrderedAbsDifference[T OrderedNumeric](a, b T) T { 75 | return T(AbsDifference[OrderedAbs[T]](OrderedAbs[T](a), OrderedAbs[T](b))) 76 | } 77 | 78 | // ComplexAbsDifference returns the absolute value of the difference 79 | // between a and b, where a and b are of a complex type. 80 | func ComplexAbsDifference[T Complex](a, b T) T { 81 | return T(AbsDifference[ComplexAbs[T]](ComplexAbs[T](a), ComplexAbs[T](b))) 82 | } 83 | 84 | func asSame[T any](_ T, b interface{}) T { 85 | return b.(T) 86 | } 87 | 88 | // GeneralAbsDifference implements AbsDifference for any *built-in* numeric type T. 89 | // 90 | // However, it panics for defined numeric types that are not built-in: 91 | // handling those cases under the current design would require the use of reflection. 92 | func GeneralAbsDifference[T Numeric](a, b T) T { 93 | switch a := (interface{})(a).(type) { 94 | case int: 95 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 96 | case int8: 97 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 98 | case int16: 99 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 100 | case int32: 101 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 102 | case int64: 103 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 104 | case uint: 105 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 106 | case uint8: 107 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 108 | case uint16: 109 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 110 | case uint32: 111 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 112 | case uint64: 113 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 114 | case uintptr: 115 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 116 | case float32: 117 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 118 | case float64: 119 | return asSame(b, OrderedAbsDifference(a, asSame(a, b))) 120 | 121 | case complex64: 122 | return asSame(b, ComplexAbsDifference(a, asSame(a, b))) 123 | case complex128: 124 | return asSame(b, ComplexAbsDifference(a, asSame(a, b))) 125 | 126 | default: 127 | panic(fmt.Sprintf("%T is not a builtin numeric type", a)) 128 | } 129 | } 130 | 131 | type MyInt int 132 | 133 | func main() { 134 | fmt.Println(GeneralAbsDifference(42, 64)) 135 | fmt.Println(GeneralAbsDifference(42+3i, 64+0i)) 136 | fmt.Println(GeneralAbsDifference(MyInt(42), MyInt(64))) 137 | } 138 | -------------------------------------------------------------------------------- /iterate/iterate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package iterate provides iterator adaptors for built-in container types. 6 | package iterate 7 | 8 | import ( 9 | "context" 10 | "errors" 11 | "io" 12 | "reflect" 13 | ) 14 | 15 | // Iterate invokes emit for each value produced by next. 16 | // If next returns io.EOF, Iterate returns nil. 17 | // If next returns any other non-nil error, Iterate immediately returns that error. 18 | func Iterate[T any](next func() (T, error), emit func(T) error) error { 19 | for { 20 | x, err := next() 21 | if err != nil { 22 | if !errors.Is(err, io.EOF) { 23 | return err 24 | } 25 | return nil 26 | } 27 | if err := emit(x); err != nil { 28 | return err 29 | } 30 | } 31 | } 32 | 33 | // Transform transforms the values produced by next by applying f to each value. 34 | func Transform[T, U any](next func() (T, error), f func(T) (U, error)) func() (U, error) { 35 | return func () (U, error) { 36 | x, err := next() 37 | if err != nil { 38 | return *new(U), err 39 | } 40 | return f(x) 41 | } 42 | } 43 | 44 | // Reduce iterates next until EOF, accumulating the results using f with initial value init. 45 | func Reduce[T, U any](next func() (T, error), init U, f func(T, U) (U, error)) (U, error) { 46 | y := init 47 | for { 48 | x, err := next() 49 | if err != nil { 50 | if !errors.Is(err, io.EOF) { 51 | return y, err 52 | } 53 | break 54 | } 55 | y, err = f(x, y) 56 | if err != nil { 57 | return y, err 58 | } 59 | } 60 | return y, nil 61 | } 62 | 63 | // SliceElems returns an iterator function that produces each successive element of s. 64 | func SliceElems[T any](s []T) func() (T, error) { 65 | i := 0 66 | return func() (T, error) { 67 | if i >= len(s) { 68 | return *new(T), io.EOF 69 | } 70 | 71 | x := s[i] 72 | i++ 73 | return x, nil 74 | } 75 | } 76 | 77 | // ToSlice returns a slice containing the elements produced by next. 78 | func ToSlice[T any](next func() (T, error)) ([]T, error) { 79 | var s []T 80 | for { 81 | x, err := next() 82 | if err != nil { 83 | if !errors.Is(err, io.EOF) { 84 | return s, err 85 | } 86 | return s, nil 87 | } 88 | s = append(s, x) 89 | } 90 | } 91 | 92 | // MapKeys returns an iterator function that produces each successive element 93 | // of m, in arbitrary order. 94 | func MapKeys[K comparable, V any](m map[K]V) func() (K, error) { 95 | iter := reflect.ValueOf(m).MapRange() 96 | if !iter.Next() { 97 | iter = nil 98 | } 99 | return func() (K, error) { 100 | if iter == nil { 101 | return *new(K), io.EOF 102 | } 103 | k, _ := iter.Key().Interface().(K) 104 | if !iter.Next() { 105 | iter = nil 106 | } 107 | return k, nil 108 | } 109 | } 110 | 111 | // MapElems returns an iterator function that produces each successive element 112 | // of m, in arbitrary order. 113 | func MapElems[K comparable, V any](m map[K]V) func() (V, error) { 114 | iter := reflect.ValueOf(m).MapRange() 115 | if !iter.Next() { 116 | iter = nil 117 | } 118 | return func() (V, error) { 119 | if iter == nil { 120 | return *new(V), io.EOF 121 | } 122 | v, _ := iter.Value().Interface().(V) 123 | if !iter.Next() { 124 | iter = nil 125 | } 126 | return v, nil 127 | } 128 | } 129 | 130 | // Map returns an iterator function that produces each successive key–value pair 131 | // of m, in arbitrary order. 132 | func Map[K comparable, V any](m map[K]V) func() (K, V, error) { 133 | iter := reflect.ValueOf(m).MapRange() 134 | if !iter.Next() { 135 | iter = nil 136 | } 137 | return func() (K, V, error) { 138 | if iter == nil { 139 | return *new(K), *new(V), io.EOF 140 | } 141 | k, _ := iter.Key().Interface().(K) 142 | v, _ := iter.Value().Interface().(V) 143 | if !iter.Next() { 144 | iter = nil 145 | } 146 | return k, v, nil 147 | } 148 | } 149 | 150 | // Chan returns an iterator function that returns each successive element 151 | // received from c. 152 | func Chan[T any](c <-chan T) func() (T, error) { 153 | return func() (T, error) { 154 | x, ok := <- c 155 | if !ok { 156 | return x, io.EOF 157 | } 158 | return x, nil 159 | } 160 | } 161 | 162 | // Chan returns an iterator function that returns each successive element 163 | // received from c, or the zero T and ctx.Err() if ctx is done and no value is 164 | // ready to receive. 165 | func ChanCtx[T any](ctx context.Context, c <-chan T) func() (T, error) { 166 | return func() (T, error) { 167 | select { 168 | case <-ctx.Done(): 169 | return *new(T), ctx.Err() 170 | case x, ok := <-c: 171 | if !ok { 172 | return x, io.EOF 173 | } 174 | return x, nil 175 | } 176 | } 177 | } 178 | 179 | // ToChan repeatedly produces a value using next and sends that value on c, 180 | // until next returns a non-nil error. 181 | // 182 | // ToChan does not close the channel. 183 | // The caller may close it after ToChan returns. 184 | // 185 | // If the error returned by next is io.EOF, ToChan returns nil. 186 | // Otherwise, ToChan returns the error returned by next. 187 | func ToChan[T any](c chan<- T, next func() (T, error)) error { 188 | for { 189 | x, err := next() 190 | if err != nil { 191 | if !errors.Is(err, io.EOF) { 192 | return err 193 | } 194 | return nil 195 | } 196 | 197 | c <- x 198 | } 199 | } 200 | 201 | // ToChanCtx repeatedly produces a value using next and sends that value on c, 202 | // until either ctx is done or next returns a non-nil error. 203 | // 204 | // ToChanCtx does not close the channel. 205 | // The caller may close it after ToChanCtx returns. 206 | // 207 | // If the final error from next is io.EOF, 208 | // ToChanCtx returns the final value from next and a nil error. 209 | // If ctx becomes done after a call to next returned a nil error, 210 | // ToChanCtx returns the final (unsent) value from next and ctx.Err(). 211 | // Otherwise, ToChanCtx returns the final value and error from next. 212 | // 213 | func ToChanCtx[T any](ctx context.Context, c chan<- T, next func() (T, error)) (T, error) { 214 | for { 215 | x, err := next() 216 | if err != nil { 217 | if !errors.Is(err, io.EOF) { 218 | return x, err 219 | } 220 | return x, nil 221 | } 222 | 223 | select { 224 | case <-ctx.Done(): 225 | return x, ctx.Err() 226 | case c <- x: 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /subtypes.md: -------------------------------------------------------------------------------- 1 | # A Theory of Subtyping for Go 2 | 3 | In Featherweight Go, the subtyping judgement (`Δ ⊢ τ <: σ`) plays a central 4 | role. However, subtyping is notably absent from both the [Go specification][] 5 | (which instead relies on [assignability][]) and the [Type Parameters draft][] 6 | (which uses a combination of [interface][] implementation and [type list][] 7 | matching). Here, I examine a general notion of subtyping for the full Go 8 | language, _derivable from_ assignability. 9 | 10 | ## What is a type? 11 | 12 | According to the Go specification, “[a] _[type][]_ determines a set of values 13 | together with operations and methods specific to those values”. 14 | 15 | ## What is a subtype? 16 | 17 | Formal definitions of a “subtype” vary. [Fuh and Mishra] defined subtyping 18 | “based on type embedding or coercion: type _t₁_ is a subtype of type _t₂_, 19 | written _t₁_ ▹ _t₂_, if we have some way of mapping every value 20 | with type _t₁_ to a value of type _t₂_.” That would seem to correspond to Go's 21 | notion of [conversion][] operation. 22 | 23 | However, [Reynolds][] defined subtypes in terms of only _implicit_ conversions: 24 | “When there is an implicit conversion from sort ω to sort ω′, we write ω ≤ ω′ 25 | and say that ω is a _subsort_ (or _subtype_) of ω′. Syntactically, this means 26 | that a phrase of sort ω can occur in any context which permits a phrase of sort 27 | ω′.” This definition maps more closely to Go's assignability, and to the 28 | “implements” notion of subtyping used in [Featherweight Go][], and it is the 29 | interpretation I will use here. 30 | 31 | Notably, all commonly-used definitions of subtyping are reflexive: a type τ is 32 | always a subtype of itself. 33 | 34 | ## What is a subtype _in Go_? 35 | 36 | Go allows implicit conversion of a value `x` to type `T` only when `x` is 37 | “assignable to” `T`. Following Reynolds' definition, I interpret a “context 38 | which permits a phrase of sort ω′” in Go as “an operation for which ω′ is 39 | assignable to the required operand type”. That leads to the following 40 | definition: 41 | 42 | In Go, `T1` is a _subtype_ of `T2` if, for every `T3` to which a value of type 43 | `T2` is assignable, every value `x` of type `T1` is _also_ assignable to `T3`. 44 | (Note that the precondition is “`x` _of type_ `T1`”, not “`x` _assignable to_ 45 | `T1`”. The distinction is subtle, but important.) 46 | 47 | Let's examine the assignability cases in the Go specification. 48 | 49 | > A value `x` is _assignable_ to a variable of type `T` ("`x` is assignable to 50 | > `T`") if one of the following conditions applies: 51 | 52 | -------------------------------------------------------------------------------- 53 | 54 | > * `x`'s type is identical to `T` 55 | 56 | This gives the two reflexive rules from the Featherweight Go paper: 57 | 58 | ``` 59 | ---------- 60 | Δ ⊢ α <: α 61 | ``` 62 | 63 | ``` 64 | ------------ 65 | Δ ⊢ τS <: τS 66 | ``` 67 | 68 | For the full Go language, we can generalize `τS` to include the built-in 69 | non-interface [composite types][] (arrays, structs, pointers, functions, slices, 70 | maps, and channels), boolean types, numeric types, and string types, which we 71 | collectively call the _[concrete types][]_. 72 | 73 | This part of the assignability definition also allows a (redundant) rule for 74 | interface types, which is omitted from Featherweight Go: 75 | 76 | ``` 77 | ------------ 78 | Δ ⊢ τI <: τI 79 | ``` 80 | 81 | -------------------------------------------------------------------------------- 82 | 83 | > * `x`'s type `V` and `T` have identical [underlying types][] and at least 84 | > one of `V` or `T` is not a [defined][] type. 85 | 86 | This case could in theory make each [literal][] type a subtype of every 87 | _[defined][]_ type, provided that the defined type has no methods (and thus 88 | cannot be assigned to any interface with a non-empty method set). 89 | 90 | ``` 91 | literal(τ) Δ ⊢ underlying(σ) = τ methodsΔ(σ) = ∅ 92 | ---------------------------------------------------- 93 | Δ ⊢ τ <: σ 94 | ``` 95 | 96 | However, this rule is inadmissibly fragile: if a method were to be added to `T`, 97 | as is allowed by the [Go 1 compatibility guidelines][], then `V` would cease to 98 | be a subtype of `T`. Although there may be a subtype relationship today between 99 | such types, no program should rely on it. (Consider an 100 | [example](https://play.golang.org/p/2kU0IA-0zmn) in which a defined type gains a 101 | trivial `String` method.) 102 | 103 | -------------------------------------------------------------------------------- 104 | 105 | > * `T` is an interface type and `x` [implements][] `T`. 106 | 107 | This leads to the interface-subtyping rule `<:I` from Featherweight Go: 108 | 109 | ``` 110 | methodsΔ(τ) ⊇ methodsΔ(τI) 111 | -------------------------- 112 | Δ ⊢ τ <: τI 113 | ``` 114 | 115 | -------------------------------------------------------------------------------- 116 | 117 | > * `x` is a bidirectional channel value, `T` is a channel type, `x`'s type 118 | > `V` and `T` have identical element types, and at least one of `V` or `T` 119 | > is not a defined type. 120 | 121 | This case is analogous to the case for “identical underlying types”. It makes 122 | the literal type `chan T` a subtype of both `<-chan T` and `chan<- T`, and by 123 | extension a subtype of any defined type with `<-chan T` or `chan<- T` as its 124 | underlying type. 125 | 126 | ``` 127 | ---------------------- 128 | Δ ⊢ chan τ <: <-chan τ 129 | ``` 130 | 131 | ``` 132 | ---------------------- 133 | Δ ⊢ chan τ <: chan<- τ 134 | ``` 135 | 136 | Unlike a defined type, a literal channel type with a direction can never acquire 137 | methods in the future. However, we cannot exclude the possibility of new 138 | interface types themselves: for example, if [sum types][] are ever added to the 139 | language, `chan T` could not reasonably be assignable to a sum type that allows 140 | both `<-chan T` and `chan<- T`, since we could not determine to which of those 141 | types it should decay. However, each of `<-chan T` and `chan<- T` could 142 | individually be assignable to such a sum. 143 | 144 | -------------------------------------------------------------------------------- 145 | 146 | > * `x` is the predeclared identifier `nil` and `T` is a pointer, function, 147 | > slice, map, channel, or interface type. 148 | 149 | The predeclared identifier `nil` does not itself have a type, so this case does 150 | not contribute any subtypes. (If it did, then the `nil` type would be a subtype 151 | of all pointer, function, slice, map, channel, and interface types.) 152 | 153 | -------------------------------------------------------------------------------- 154 | 155 | > * `x` is an untyped [constant][] [representable][] by a value of type `T`. 156 | 157 | As with `nil`, this case does not contribute any subtypes. 158 | 159 | ## Conclusion 160 | 161 | Having examined the above cases, we find: 162 | 163 | * A type `T` is always a subtype of itself. 164 | * A type `T` is a subtype of every interface that it implements. 165 | * Additional subtype relationships exist, but are too fragile to rely on. 166 | * A literal composite type is a subtype of any defined type that has it as 167 | an underlying type _and_ has no methods, but Go 1 compatibility allows 168 | methods to be added to defined types. 169 | * A bidirectional channel type literal is a subtype of the corresponding 170 | directional channel type literals, but only provided that sum types are 171 | not added to the language. 172 | 173 | Thus, the only _useful_ subtyping relationship in Go is: “`T1` is a subtype of 174 | `T2` if `T1` _is_ or _implements_ `T2`“. 175 | 176 | ## Appendix: Why “of type” instead of “assignable to”? 177 | 178 | I could have chosen a different definition of subtyping, based on _assignability 179 | to_ type `T1` instead of a value _of_ type `T1`. That would give a rule 180 | something like: “`T1` is a subtype of `T2` if, for every value `x` _assignable 181 | to_ `T1`, `x` is also assignable to `T2`.“ Why not use that definition instead? 182 | 183 | Unfortunately, that alternate definition would mean that even interface types do 184 | not have subtypes: if [defined][] type `T` has a [literal][] underlying type `U` 185 | and implements inteface `I`, then a value of type `U` is assignable to `T`, but 186 | not to `I`, so `T` could not be a subtype of `I`. A notion of subtyping that 187 | does not even capture simple interface-implementation matching does not seem 188 | useful. 189 | 190 | 191 | 192 | [Go specification]: https://golang.org/ref/spec 193 | [type]: https://golang.org/ref/spec#Types 194 | [underlying types]: https://golang.org/ref/spec#Types 195 | [composite types]: https://golang.org/ref/spec#Types 196 | [literal]: https://golang.org/ref/spec#Types 197 | [concrete types]: https://golang.org/ref/spec#Variables 198 | [type switch]: https://golang.org/ref/spec#Type_switches 199 | [defined]: https://golang.org/ref/spec#Type_definitions 200 | [assignability]: https://golang.org/ref/spec#Assignability 201 | [conversion]: https://golang.org/ref/spec#Conversions 202 | [interface]: https://golang.org/ref/spec#Interface_types 203 | [implements]: https://golang.org/ref/spec#Interface_types 204 | [constants]: https://golang.org/ref/spec#Constants 205 | [constant]: https://golang.org/ref/spec#Constants 206 | [representable]: https://golang.org/ref/spec#Representability 207 | [Type Parameters draft]: https://golang.org/design/go2draft-type-parameters 208 | [type list]: https://golang.org/design/go2draft-type-parameters#type-lists-in-constraints 209 | [sum types]: https://golang.org/issue/19412 210 | 211 | 212 | 213 | [Fuh and Mishra]: https://link.springer.com/content/pdf/10.1007/3-540-19027-9_7.pdf "Type Inference with Subtypes, 1988" 214 | [Reynolds]: https://link.springer.com/content/pdf/10.1007%2F3-540-10250-7_24.pdf "Using Category Theory to Design Implicit Conversions and Generic Operators, 1980" 215 | [Featherweight Go]: https://arxiv.org/abs/2005.11710 "Featherweight Go, 2020" 216 | -------------------------------------------------------------------------------- /typelist.md: -------------------------------------------------------------------------------- 1 | # Orthogonalizing Type Lists 2 | 3 | The [type list][] interfaces in the Type Parameters [Type Parameters draft][] 4 | lack significant properties common to other interface types — and, indeed, types 5 | in general! — in Go as it exists today. 6 | 7 | ## Type lists are not coherent with interface types. 8 | 9 | The draft specifies that “[i]nterface types with type lists may only be used as 10 | constraints on type parameters. They may not be used as ordinary interface 11 | types. The same is true of the predeclared interface type `comparable`. … This 12 | restriction may be lifted in future language versions.” However, it would not be 13 | possible to lift this restriction without giving type-list interfaces a 14 | _different_ meaning as interface types than they have as type constraints. 15 | 16 | An ordinary interface type `T` [implements][] itself: for all types `T`, all 17 | values of `T` support the operations of `T` and are assignable to `T`. However, 18 | a type-list interface _cannot_ “implement” itself for the purpose of satisfying 19 | type constraints: that would provide unspecified (and perhaps inconsistent) 20 | semantics for operators such as `+` and `<` when two variables of the type store 21 | values with different [concrete types][], and the whole point of type-list 22 | interfaces in the first place is to be able to safely allow those operators. 23 | 24 | According to the Go specification, “[a] _[type][]_ determines a set of values 25 | together with operations and methods specific to those values”. But a type list, 26 | as defined in the draft, represents a set of _sets of_ values paired with 27 | operations whose semantics vary per set, not a single set of values with uniform 28 | operations. So in some sense, a type-list interface is not really even a type! 29 | 30 | This mismatch in meaning not only makes interface types and type-lists more 31 | complex to describe and reason about, but also limits the future extensibility 32 | of the language. Without this semantic mismatch, the Type Parameters design 33 | could be extended to allow type variables themselves as constraints, with the 34 | meaning that “the argument _is_ or _implements_ the constraint type”, or perhaps 35 | “the argument _is assignable to_ the constraint type”. However, if the meaning 36 | of an interface varies depending on whether it is a type or a constraint the 37 | [substitution lemma][] would no longer hold: a type-list interface passed as 38 | parameter `Τ` and interpreted as a proper interface type would match any type 39 | that “implements” `T` — including a type-list interface naming any subset of 40 | `T`'s type list — but upon substituting `T`'s actual type for the constraint, it 41 | would no longer allow those interface types. 42 | 43 | In contrast, the built-in `comparable` constraint, if allowed as an ordinary 44 | interface type, would have the same properties as other interface types: the 45 | `==` and `!=` operations are defined uniformly and meaningfully for every pair 46 | of `comparable` values, so the type `comparable` itself guarantees the 47 | operations enabled by the `comparable` constraint. 48 | 49 | ## Type lists miss the opportunity to address general use-cases. 50 | 51 | Type lists match on the underlying type of a type argument, but do not allow a 52 | generic function to convert the argument type to its underlying type. That 53 | prevents even simple type-based specializations, such as in the 54 | `GeneralAbsDifference` example in the draft. 55 | 56 | Type lists enable the use of operators that are not defined for interface types. 57 | However, they fail to cover other use cases that require non-interface types 58 | (such as 59 | [wrappers around `atomic.Value`](https://go2goplay.golang.org/p/1bfbQ1MDy6i), or 60 | types that embed a field of type `*T`). (Admittedly, these use-cases are rare.) 61 | 62 | Type-list interfaces also have a clear similarity to the oft-requested 63 | “[sum types][]” that could be used to enable safer type-switches, but because 64 | type-lists match on underlying types instead of concrete types, they do not 65 | directly address the use cases for sum types. 66 | 67 | ## How could we address the use-cases of type lists more orthogonally? 68 | 69 | Type lists could be made more orthogonal in one of several ways, but we must 70 | start by acknowledging that one of the type constraints we want to express — 71 | namely, the constraint that a parameter must be a _non-interface_ type — is not 72 | itself a coherent Go type. 73 | 74 | We could make a minimal change to the design to distinguish between types and 75 | constraints: 76 | 77 | 1. Define that a type constraint may be either an interface type or a type-list 78 | constraint. Define type-list constraints as constraints (not types) using 79 | tokens other than `type` and `interface`. 80 | 81 | For example, use the token `constraint`instead: 82 | 83 | ```go 84 | constraint T { 85 | type X, Y, Z 86 | someInterface 87 | } 88 | ``` 89 | 90 | Or, we could split out the “must be a non-interface type” constraint, and 91 | preserve as much of the current type-lists as we can as interface types: 92 | 93 | 1. Define “type list interface types”, implemented by any type for which all 94 | values are known to have _a dynamic type with the same underlying type as_ 95 | one of the types in the list. Either reject or filter out any interface 96 | types in the list, since an interface type cannot be the dynamic underlying 97 | type of any value at run time. 98 | 99 | 2. Define that a type constraint may be either a type, or a type restricted to 100 | its concrete (non-interface) implementations. 101 | 102 | Or we could break down type-lists into smaller orthogonal parts: disjunction 103 | interfaces, underlying-type interfaces, and concrete-type constraints. 104 | 105 | 1. Define “sum interface types”, implemented by any type that _is or 106 | implements_ any of the listed types. 107 | 108 | 2. Define “defined-sum interface types”, implemented by any type whose 109 | underlying type is any of the listed types. 110 | 111 | 3. Define a mapping between defined-sum interface types and the corresponding 112 | ordinary sum types. 113 | 114 | 4. Define that a type constraint may be either a type, or a type restricted to 115 | its concrete (non-interface) implementations. 116 | 117 | Under the orthogonal option, the interface types would be defined as follows. 118 | 119 | ### Sum interface types 120 | 121 | A sum-interface type (or “sum type”) is declared using the syntax `type = T1, …, 122 | Tn`, and is implemented by any type that _is_ or _implements_ at least one of 123 | the types in the list. (When a sum type `T` is implemented by a concrete type 124 | `R`, we say that `R` is “in“ the sum `T`.) 125 | 126 | The method set of a sum type is the intersection of the method sets of each type 127 | in the sum. No other methods may be defined and no other interfaces may be 128 | embedded, because the interfaces and methods implemented by the types in the sum 129 | are already known. 130 | 131 | The zero value of a sum type is the `nil` interface value, even if none of the 132 | types in the list is itself an interface type. 133 | 134 | A type switch or type assertion on a variable of a sum type may use only the 135 | types in the sum and interface types _implemented by_ at least one type in the 136 | sum. To allow lists to be expanded over time, a `default` case is permitted even 137 | if the switch is exhaustive. 138 | 139 | A sum type is assignable to any interface implemented by all of the types in the 140 | sum. 141 | 142 | If all of the types in the sum are convertible to a type `T`, then the sum type 143 | is also convertible to `T`. If `T` is a concrete type, the `nil` interface value 144 | converts to the zero-value of the type. 145 | 146 | A sum type embedded in another interface `S` restricts `S` to the types in the 147 | sum that also implement the remainder of `S`. (If multiple sum interface types 148 | are embedded in an interface, the types in the sums are intersected.) 149 | 150 | ### Defined-sum interface types 151 | 152 | A defined-sum interface type (or “defined sum type”), declared using the syntax 153 | `type T1, … Tn`, is a sum type that includes in the sum every type whose 154 | underlying type is one of the listed types (`T1` through `Tn`). (An ordinary sum 155 | type includes a fixed set of types, but the set of types included in a 156 | defined-sum type is unbounded.) 157 | 158 | The underlying types listed in a defined sum type declaration must be 159 | predeclared boolean, numeric, or string types, type literals, or sum types 160 | comprising the same. 161 | 162 | A defined-sum interface type _may_ include additional methods. A defined-sum 163 | interface may embed other (ordinary and defined) sum interfaces. 164 | 165 | A type `R` implements a defined sum type `T` if the underlying type of `R` is in 166 | the underlying-type list of `T`, the method set of `R` includes all of the 167 | methods declared in `T`, and `R` implements all of the interfaces embedded in 168 | `T`. 169 | 170 | ### The `Underlying` type alias and `underlying` function 171 | 172 | The built-in type `Underlying(type T)` is an alias for the type encompassing the 173 | underlying types of the _concrete values of_ any type `T`: 174 | 175 | * If `T` is a sum interface type (including a defined-sum interface type), 176 | `Underlying(T)` is the sum interface type containing `Underlying(Tᵢ)` for 177 | each `Tᵢ` in `T`. 178 | * If `T` is any other interface type, `Underlying(T)` is the empty interface 179 | type. (That is, the set of possible underlying types for the values of type 180 | `T` is unrestricted.) 181 | * Otherwise, `T` is a concrete type, and `Underlying(T)` is its underlying 182 | type. 183 | 184 | The built-in function `underlying` converts a value of any type to a value of 185 | its underlying type. 186 | 187 | ```go 188 | func underlying(type T)(x T) Underlying(T) 189 | ``` 190 | 191 | ### Constraining type parameters to concrete types 192 | 193 | The last piece of the puzzle is a constraint that restricts a type parameter to 194 | only [concrete types][]. If all of the concrete types in a sum interface type 195 | support a given operation, then a function with a type parameter constrained to 196 | those concrete types can safely use that operation. 197 | 198 | We could imagine a lot of options for the syntax of such a constraint. For the 199 | purpose of this document, I suggest the keyword `in` immediately before the 200 | parameter's type constraint, meaning “the type must be one of the concrete 201 | dynamic types for values stored _in_ the constraint type”. (However, note that 202 | this constraint has a well-defined meaning even for types that are not sum 203 | types.) 204 | 205 | ## Examples 206 | 207 | With the orthogonal approach, the examples in the draft design become a bit more 208 | verbose, but more explicit — and, notably, the GeneralAbsDifference function 209 | becomes possible to implement. 210 | 211 | ```go 212 | package constraints 213 | 214 | type Ordered interface { 215 | type int, int8, …, string 216 | } 217 | ``` 218 | 219 | ```go 220 | func Smallest(type T in constraints.Ordered)(s []T) T { 221 | … 222 | } 223 | ``` 224 | 225 | ```go 226 | // StringableSignedInteger is an interface type implemented by any 227 | // type that both 1) is defined as a signed integer type 228 | // (or a sum type comprising signed integer types), and 229 | // 2) has a String method. 230 | type StringableSignedInteger interface { 231 | type int, int8, int16, int32, int64 232 | String() string 233 | } 234 | ``` 235 | 236 | ```go 237 | // SliceConstraint is an interface type implemented by any slice type with 238 | // an element type identical to the type parameter. 239 | type SliceConstraint(type T) interface { 240 | type []T 241 | } 242 | ``` 243 | 244 | ```go 245 | // integer is a type implemented by any defined integer type. 246 | type integer interface { 247 | type int, int8, int16, int32, int64, 248 | uint, uint8, uint16, uint32, uint64, uintptr 249 | } 250 | 251 | // Convert converts a value of type From to the type To. 252 | // To must be a specific concrete integer type. 253 | // From may be any type that implements the integer interface, 254 | // including any sum type whose members are all integer types. 255 | func Convert(type To in integer, From integer)(x From) To { 256 | to := To(x) 257 | if From(to) != x { 258 | panic("conversion out of range") 259 | } 260 | return to 261 | } 262 | ``` 263 | 264 | ```go 265 | type integer interface { 266 | type int, int8, int16, int32, int64, 267 | uint, uint8, uint16, uint32, uint64, uintptr 268 | } 269 | 270 | func Add10(type T in integer)(s []T) { 271 | for i, v := range s { 272 | s[i] = v + 10 // OK: 10 can convert to any concrete integer type 273 | } 274 | } 275 | 276 | // This function is INVALID. 277 | func Add1024(type T in integer)(s []T) { 278 | for i, v := range s { 279 | s[i] = v + 1024 // INVALID: 1024 not permitted by types int8 and uint8 in integer 280 | } 281 | } 282 | 283 | // This function is INVALID. 284 | func Add10Interface(type T integer)(s []T) { 285 | for i, v := range s { 286 | s[i] = v + 10 // INVALID: operation + not permitted for type T that may be an interface type (use "T in integer" to restrict to concrete types) 287 | } 288 | } 289 | 290 | ``` 291 | 292 | ```go 293 | type Numeric interface { 294 | type int, int8, int16, int32, int64, 295 | uint, uint8, uint16, uint32, uint64, uintptr, 296 | float32, float64, 297 | complex64, complex128 298 | } 299 | 300 | // GeneralAbsDifference computes the absolute difference between two values 301 | // of any concrete integer type T. 302 | func GeneralAbsDifference(type T in Numeric)(a, b T) T { 303 | // T is a concrete type, so *T can be converted to *Underlying(T). 304 | // The set of possible types for T is infinite (any defined integer type), 305 | // but the set of possible types for Underlying(T) is small 306 | // (only the built-in integer types). 307 | var result T 308 | var rp interface{} = (*Underlying(T))(&result) 309 | 310 | // Convert a and b to their underlying types so that we 311 | // can type-assert them to a concrete type from a finite list. 312 | var au, bu interface{} = underlying(a), underlying(b) 313 | 314 | // Now we can write an exhaustive type switch over the list of possible types of 315 | // rp, au, and bu. 316 | switch rp := rp.(type) { 317 | case *int: 318 | *rp = OrderedAbsDifference(au.(int), bu.(int)) 319 | case *int8: 320 | *rp = OrderedAbsDifference(au.(int8), bu.(int8)) 321 | … 322 | 323 | case *complex64: 324 | *rp = ComplexAbsDifference(au.(complex64), bu.(complex64)) 325 | … 326 | 327 | default: 328 | // If, say, int128 is added to a future version of the language, 329 | // we will need to add a case for it. 330 | panic(fmt.Sprintf("%T is not a recognized numeric type", au)) 331 | } 332 | 333 | return result 334 | } 335 | ``` 336 | 337 | [type]: https://golang.org/ref/spec#Types 338 | [implements]: https://golang.org/ref/spec#Interface_types 339 | [concrete type]: https://golang.org/ref/spec#Variables 340 | [concrete types]: https://golang.org/ref/spec#Variables 341 | [conversions]: https://golang.org/ref/spec#Conversions 342 | [sum types]: https://golang.org/issue/19412 343 | [Type Parameters draft]: https://golang.org/design/go2draft-type-parameters 344 | [type list]: https://golang.org/design/go2draft-type-parameters#type-lists-in-constraints 345 | [pointer methods]: https://golang.org/design/go2draft-type-parameters#pointer-methods 346 | [substitution lemma]: http://twelf.org/wiki/Substitution_lemma 347 | [Featherweight Go]: https://arxiv.org/abs/2005.11710 "Featherweight Go, 2020" 348 | --------------------------------------------------------------------------------