qinghe il y a 3 ans
révision
4d7f96c479
14 fichiers modifiés avec 578 ajouts et 0 suppressions
  1. 17
    0
      codes/any.go
  2. 27
    0
      codes/comparable.go
  3. 37
    0
      codes/generic_stack.go
  4. 32
    0
      codes/graph.go
  5. 26
    0
      codes/method.go
  6. 16
    0
      codes/node.go
  7. 42
    0
      codes/operator.go
  8. 14
    0
      codes/stack.go
  9. 11
    0
      codes/stringify.go
  10. 66
    0
      codes/type_sets.go
  11. 34
    0
      codes/use_constraints.go
  12. 10
    0
      go.mod
  13. 8
    0
      go.sum
  14. 238
    0
      main.slide

+ 17
- 0
codes/any.go Voir le fichier

@@ -0,0 +1,17 @@
1
+package main
2
+
3
+// ANY OMIT
4
+// any is a type alias of empty interface
5
+type any = interface{}
6
+
7
+// ANY OMIT
8
+
9
+// PRINT OMIT
10
+// Print prints the elements of any slice.
11
+// Print has a type parameter T and has a single (non-type)
12
+// parameter s which is a slice of that type parameter.
13
+func Print[T interface{}](s []T) {
14
+	// same as above
15
+}
16
+
17
+// PRINT OMIT

+ 27
- 0
codes/comparable.go Voir le fichier

@@ -0,0 +1,27 @@
1
+package main
2
+
3
+// COMPARABLE EXAMPLE OMIT
4
+// Index returns the index of x in s, or -1 if not found.
5
+func Index[T comparable](s []T, x T) int {
6
+	for i, v := range s {
7
+		// v and x are type T, which has the comparable
8
+		// constraint, so we can use == here.
9
+		if v == x {
10
+			return i
11
+		}
12
+	}
13
+	return -1
14
+}
15
+
16
+// COMPARABLE EXAMPLE OMIT
17
+
18
+
19
+// ComparableHasher OMIT
20
+// ComparableHasher is a type constraint that matches all
21
+// comparable types with a Hash method.
22
+type ComparableHasher interface {
23
+	comparable
24
+	Hash() uintptr
25
+}
26
+
27
+// ComparableHasher OMIT

+ 37
- 0
codes/generic_stack.go Voir le fichier

@@ -0,0 +1,37 @@
1
+package main
2
+
3
+// STACK OMIT
4
+type Stack[T any] []T
5
+
6
+type Node[T any] struct {
7
+	val T
8
+}
9
+
10
+// STACK OMIT
11
+
12
+// INTSTACK OMIT
13
+var stack Stack[int]       // underlying type: []int
14
+var stack = Stack{1, 2, 3} // INVALID: cannot use generic type Stack[T any] without instantiation
15
+// INTSTACK OMIT
16
+
17
+// TYPE OMIT
18
+// make
19
+var stack = make(Stack[int])
20
+// new
21
+var stack = new(Stack[int])
22
+// 强制类型转换
23
+var stack = Stack[int]{1, 2, 3}
24
+var intStack []int = []int(stack)
25
+
26
+// TYPE OMIT
27
+
28
+// METHOD OMIT
29
+func (s *Stack[T]) Push(v T) { *s = append(*s, v) }
30
+
31
+func (s *Stack[T]) Pop() T {
32
+	v := *s[len(*s)-1]
33
+	*s = *s[:len(*s)-1]
34
+	return v
35
+}
36
+
37
+// METHOD OMIT

+ 32
- 0
codes/graph.go Voir le fichier

@@ -0,0 +1,32 @@
1
+
2
+package main
3
+
4
+
5
+// GRAPH OMIT
6
+// NodeConstraint is the type constraint for graph nodes:
7
+// they must have an Edges method that returns the Edge's
8
+// that connect to this Node.
9
+type NodeConstraint[Edge any] interface {
10
+	Edges() []Edge
11
+}
12
+
13
+// EdgeConstraint is the type constraint for graph edges:
14
+// they must have a Nodes method that returns the two Nodes
15
+// that this edge connects.
16
+type EdgeConstraint[Node any] interface {
17
+	Nodes() (from, to Node)
18
+}
19
+
20
+// Graph is a graph composed of nodes and edges.
21
+type Graph[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]] struct { ... }
22
+
23
+// New returns a new graph given a list of nodes.
24
+func New[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]] (nodes []Node) *Graph[Node, Edge] {
25
+	...
26
+}
27
+
28
+// ShortestPath returns the shortest path between two nodes,
29
+// as a list of edges.
30
+func (g *Graph[Node, Edge]) ShortestPath(from, to Node) []Edge { ... }
31
+
32
+// GRAPH OMIT

+ 26
- 0
codes/method.go Voir le fichier

@@ -0,0 +1,26 @@
1
+package main
2
+
3
+import "fmt"
4
+
5
+// START OMIT
6
+type P[T any] struct{}
7
+
8
+func (p *P[T]) Say[S any](v S) { fmt.Println(v) }
9
+
10
+type T struct{}
11
+
12
+func (t *T) Say[S any](v S) { fmt.Println(v) }
13
+
14
+func main() {
15
+	var t T
16
+	t.Say[int](1)
17
+
18
+	/** output:
19
+	method.go:8:19: methods cannot have type parameters
20
+	method.go:8:20: invalid AST: method must have no type parameters
21
+	method.go:12:16: methods cannot have type parameters
22
+	method.go:12:17: invalid AST: method must have no type parameters
23
+	*/
24
+}
25
+
26
+// END OMIT

+ 16
- 0
codes/node.go Voir le fichier

@@ -0,0 +1,16 @@
1
+package main
2
+
3
+import "fmt"
4
+
5
+// NODE OMIT
6
+type Node[T any] struct {
7
+	next *Node[T]
8
+	val  T
9
+}
10
+
11
+func main() {
12
+	var a = Node[string]{next: &Node[string]{}, val: "ssss"}
13
+	fmt.Println(a)
14
+}
15
+
16
+// NODE OMIT

+ 42
- 0
codes/operator.go Voir le fichier

@@ -0,0 +1,42 @@
1
+package main
2
+
3
+// MIN OMIT
4
+func Min[T any](s []T) T {
5
+	r := s[0] // assume that s is not empty
6
+	for _, v := range s {
7
+		if v < r { // INVALID
8
+			r = v
9
+		}
10
+	}
11
+	return r
12
+}
13
+
14
+// MIN OMIT
15
+
16
+
17
+
18
+// OPERATOR CONSTRAINT OMIT
19
+
20
+// Ordered is a type constraint that matches any ordered type.
21
+// An ordered type is one that supports the <, <=, >, and >= operators.
22
+type Ordered interface {
23
+	~int | ~int8 | ~int16 | ~int32 | ~int64 |
24
+		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
25
+		~float32 | ~float64 |
26
+		~string
27
+}
28
+
29
+// OPERATOR CONSTRAINT OMIT
30
+
31
+// OPERATOR MIN OMIT
32
+func Min[T Ordered](s []T) T {
33
+	r := s[0] // assume that s is not empty
34
+	for _, v := range s {
35
+		if v < r { // INVALID
36
+			r = v
37
+		}
38
+	}
39
+	return r
40
+}
41
+
42
+// OPERATOR MIN OMIT

+ 14
- 0
codes/stack.go Voir le fichier

@@ -0,0 +1,14 @@
1
+package main
2
+
3
+// STACK OMIT
4
+type Stack []interface{}
5
+
6
+func (s *Stack) Push(v interface{}) { *s = append(*s, v) }
7
+
8
+func (s *Stack) Pop() interface{} {
9
+	v := *s[len(*s)-1]
10
+	*s = *s[:len(*s)-1]
11
+	return v
12
+}
13
+
14
+// STACK OMIT

+ 11
- 0
codes/stringify.go Voir le fichier

@@ -0,0 +1,11 @@
1
+package main
2
+
3
+//  STRINGER OMIT
4
+// Stringer is a type constraint that requires the type argument to have
5
+// a String method and permits the generic function to call String.
6
+// The String method should return a string representation of the value.
7
+type Stringer interface {
8
+	String() string
9
+}
10
+
11
+// STRINGER OMIT

+ 66
- 0
codes/type_sets.go Voir le fichier

@@ -0,0 +1,66 @@
1
+package main
2
+
3
+// TYPE OMIT
4
+type Integer interface {
5
+	int
6
+}
7
+
8
+// TYPE OMIT
9
+
10
+// INVALID OMIT
11
+// EmbeddedParameter is INVALID.
12
+type EmbeddedParameter[T any] interface {
13
+	T // INVALID: may not list a plain type parameter
14
+}
15
+// INVALID OMIT
16
+
17
+
18
+// APPRO OMIT
19
+type MyInt int 
20
+
21
+type AnyInteger interface {
22
+	~int 
23
+}
24
+// APPRO OMIT
25
+
26
+
27
+// ALL INVALID OMIT
28
+
29
+type MyString string 
30
+
31
+// ApproximateMyString is INVALID.
32
+type ApproximateMyString interface {
33
+	~MyString // INVALID: underlying type of MyString is not MyString
34
+}
35
+
36
+// ApproximateParameter is INVALID.
37
+type ApproximateParameter[T any] interface {
38
+	~T // INVALID: T is a type parameter
39
+}
40
+
41
+type MyInterface {}
42
+
43
+// ApproximateInterface is INVALID.
44
+type ApproximateInterface interface {
45
+	~MyInterface // INVALID: T is an interface type
46
+}
47
+
48
+// ALL INVALID OMIT
49
+
50
+
51
+
52
+
53
+// ONION EXAMPLE OMIT
54
+
55
+// PredeclaredSignedInteger is a constraint that matches the
56
+// five predeclared signed integer types.
57
+type PredeclaredSignedInteger interface {
58
+	int | int8 | int16 | int32 | int64
59
+}
60
+
61
+// SignedInteger is a constraint that matches any signed integer type.
62
+type SignedInteger interface {
63
+	~int | ~int8 | ~int16 | ~int32 | ~int64
64
+}
65
+
66
+// ONION EXAMPLE OMIT

+ 34
- 0
codes/use_constraints.go Voir le fichier

@@ -0,0 +1,34 @@
1
+package main
2
+
3
+// STR OMIT
4
+// Stringify calls the String method on each element of s,
5
+// and returns the results.
6
+func Stringify[T Stringer](s []T) (ret []string) {
7
+	for _, v := range s {
8
+		ret = append(ret, v.String())
9
+	}
10
+	return ret
11
+}
12
+
13
+// STR OMIT
14
+
15
+// PRT OMIT
16
+// Print2 has two type parameters and two non-type parameters.
17
+func Print2[T1, T2 any](s1 []T1, s2 []T2) { /**...*/ }
18
+
19
+// PRT OMIT
20
+
21
+// CONCAT OMIT
22
+type Stringer interface{ String() string }
23
+
24
+type Plusser interface{ Plus(string) string }
25
+
26
+func ConcatTo[S Stringer, P Plusser](s []S, p []P) []string {
27
+	r := make([]string, len(s))
28
+	for i, v := range s {
29
+		r[i] = p[i].Plus(v.String())
30
+	}
31
+	return r
32
+}
33
+
34
+// CONCAT OMIT

+ 10
- 0
go.mod Voir le fichier

@@ -0,0 +1,10 @@
1
+module go-generic
2
+
3
+go 1.17
4
+
5
+require (
6
+	github.com/yuin/goldmark v1.4.1 // indirect
7
+	golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
8
+	golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect
9
+	golang.org/x/tools v0.1.8 // indirect
10
+)

+ 8
- 0
go.sum Voir le fichier

@@ -0,0 +1,8 @@
1
+github.com/yuin/goldmark v1.4.1 h1:/vn0k+RBvwlxEmP5E7SZMqNxPhfMVFEJiykr15/0XKM=
2
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
3
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
4
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
5
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
6
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7
+golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
8
+golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=

+ 238
- 0
main.slide Voir le fichier

@@ -0,0 +1,238 @@
1
+# Go generic
2
+Qinghe
3
+21 Jan 2022
4
+
5
+## Defining constraints
6
+constraints的作用:
7
+- 规定一些类型参数可以调用的方法
8
+
9
+interface即可满足上面的需求,因此继续沿用interface类型来定义一个constraint。
10
+
11
+还是以Stringify为例,定义一个constraint:
12
+
13
+
14
+.code codes/stringify.go /STRINGER OMIT/,/STRINGER OMIT/
15
+
16
+- 与接口定义一样,定义一些方法集
17
+- 类型参数必须实现interface
18
+
19
+
20
+## any constraints
21
+
22
+- any是一个constraint,表示类型参数可以是任何类型
23
+- constraint是个interface类型,类型参数必须实现interface
24
+
25
+推断出
26
+
27
+.code codes/any.go /ANY OMIT/,/ANY OMIT/
28
+
29
+
30
+**any的本质是个空的interface**
31
+
32
+
33
+- any一开始被设计为只允许用在泛型中的类型约束上,后续经社区讨论修改后可代替interface{}用在任何地方 [issue#33232](https://github.com/golang/go/issues/33232)
34
+- any是个预定义类型,和int、string等类似
35
+- 可以被覆盖,不会影响兼容性
36
+
37
+
38
+## Using a constraint
39
+
40
+- 单个类型参数
41
+
42
+.code codes/use_constraints.go /STR OMIT/,/STR OMIT/
43
+
44
+
45
+## Using a constraint
46
+多个类型参数
47
+
48
+- 相同约束
49
+
50
+.code codes/use_constraints.go /PRT OMIT/,/PRT OMIT/
51
+
52
+- 不同约束
53
+
54
+.code codes/use_constraints.go /CONCAT OMIT/,/CONCAT OMIT/
55
+
56
+## Generic types
57
+
58
+## Generic types
59
+
60
+不光有泛型函数,也有泛型类型,在定义一些通用类型时可以用到。
61
+
62
+
63
+.code codes/generic_stack.go /STACK OMIT/,/STACK OMIT/
64
+
65
+在使用泛型类型时,必须提供类型实参,这一操作被称为「类型实例化」。
66
+
67
+.code codes/generic_stack.go /INTSTACK OMIT/,/INTSTACK OMIT/
68
+
69
+其他使用和普通类型没有区别
70
+
71
+.code codes/generic_stack.go /TYPE OMIT/,/TYPE OMIT/
72
+
73
+## Generic types 
74
+泛型类型和一般类型一样,也可以定义方法。
75
+
76
+.code codes/generic_stack.go /METHOD OMIT/,/METHOD OMIT/
77
+
78
+- 声明方法时,receiver类型的类型参数**必须**与声明receiver类型时定义的参数**个数相同**
79
+- 方法的类型参数实参名称和类型定义时的类型参数形参**名称不需要相同**,在方法中没有使用到类型时,名称可以为 "_"
80
+- 类型参数后面**没有**constraints
81
+
82
+## Generic types 
83
+方法的声明中,不能包含类型参数。以下代码均编译不通过:
84
+
85
+.play codes/method.go /START OMIT/,/END OMIT/
86
+
87
+至于原因,请见后续。
88
+
89
+## Operators
90
+
91
+
92
+## Operators
93
+由前面的例子可以看出,使用interface作为constraints时,只规定了一系列可调用的方法集。
94
+到目前为止,泛型方法能够做的事情,除了任何类型都能做的事情外,也就只有
95
+方法调用了。
96
+
97
+但是方法调用并不能够完美实现某些需求。如下:
98
+
99
+.code codes/operator.go /MIN OMIT/,/MIN OMIT/
100
+
101
+
102
+不是所有的类型都支持`<`操作符,而且`<`不是一个方法,无法通过interface来实现。
103
+
104
+## Operators
105
+
106
+- 除了两种例外情况(后面会将),所有的四则运算、大小比较、逻辑运算只允许被用在预定义类型或者底层为预定义类型的自定义类型中。
107
+- instead of saying which operators a constraint should support, we can say which types a constraint should accept. We do this by defining a type set for a constraint.
108
+
109
+
110
+## Type sets
111
+
112
+## Type sets
113
+
114
+todo
115
+
116
+## Constraint elements
117
+在interface类型中额外定义一些元素作为约束,这些元素包括:
118
+
119
+- 具体类型
120
+- 近似类型
121
+- 联合类型
122
+
123
+## Arbitrary type constraint element
124
+
125
+可以在interface中嵌入任意类型
126
+
127
+.code codes/type_sets.go /TYPE OMIT/,/TYPE OMIT/
128
+
129
+但是,类型不能是type parameter
130
+
131
+.code codes/type_sets.go /INVALID OMIT/,/INVALID OMIT/
132
+
133
+
134
+## Approximation constraint element(近似类型)
135
+
136
+**定义类型和非定义类型(defined type and undefined type)**
137
+
138
+一个定义类型是一个在某个类型定义声明中定义的类型。
139
+
140
+所有的基本类型都是定义类型。一个非定义类型一定是一个组合类型。
141
+
142
+**底层类型(underlying type)**
143
+
144
+在Go中,每个类型都有一个底层类型。规则:
145
+- 一个内置基本类型的底层类型为它自己。
146
+- unsafe标准库包中定义的Pointer类型的底层类型是它自己。(至少我们可以认为是这样。事实上,关于unsafe.Pointer类型的底层类型,官方文档中并没有清晰的说明。我们也可以认为unsafe.Pointer类型的底层类型为*T,其中T表示一个任意类型。)
147
+- 一个非定义类型(必为一个组合类型)的底层类型为它自己。
148
+- 在一个类型声明中,新声明的类型和源类型共享底层类型。
149
+
150
+来源: [go101#类型系统概述](https://gfw.go101.org/article/type-system-overview.html)
151
+
152
+## Approximation constraint element
153
+
154
+如果某个类型T1,底层类型为类型T2,则这个类型T1被称为类型T2的近似类型。一个类型的近似类型可以有多个,这些所有的近似类型集合可以
155
+用符号`~`来表示。
156
+
157
+例如:
158
+
159
+.code codes/type_sets.go /APPRO OMIT/,/APPRO OMIT/
160
+
161
+约束AnyInteger表示的类型集为所有底层类型为int的类型,其中包括MyInt类型,因此MyInt可以作为type argument参数。
162
+
163
+因为~T表示所有底层类型为T的类型,当然类型T的底层类型也应该是自己,满足这个条件的类型有:
164
+- 非定义类型
165
+- 大部分预定义类型,比如int或者string(不包括error类型)
166
+
167
+## Approximation constraint element
168
+在近似类型中,类型T不能为类型参数或者interface类型。
169
+
170
+.code codes/type_sets.go /ALL INVALID OMIT/,/ALL INVALID OMIT/
171
+
172
+## Union constraint element(联合类型)
173
+
174
+
175
+## Union constraint element
176
+第三种允许使用的约束元素是**联合类型**。这是一种新的语法结构,用竖线(|)将一系列约束元素分隔。联合类型所代表的类型集是各约束元素类型集的并集,并且各
177
+约束元素不可以重复。
178
+
179
+.code codes/type_sets.go /ONION EXAMPLE OMIT/,/ONION EXAMPLE OMIT/
180
+
181
+
182
+## Operations based on type sets
183
+
184
+提出type sets的目的就是允许泛型函数使用'<'、'>'之类的运算符。
185
+
186
+泛型函数中允许的操作是type sets中所有的类型都允许的操作。因此,一般来说,**约束中
187
+包括的类型越多,那么允许的操作就越少,包括的类型越少,则允许的操作就越多**。
188
+
189
+## Operations based on type sets
190
+
191
+回过头再来看之前的`Min`函数的例子,可以使用如下的约束:
192
+
193
+.code codes/operator.go /OPERATOR CONSTRAINT OMIT/,/OPERATOR CONSTRAINT OMIT/
194
+
195
+现在,下面的代码就可以运行了
196
+
197
+.code codes/operator.go /OPERATOR MIN OMIT/,/OPERATOR MIN OMIT/
198
+
199
+## Comparable types in constraints
200
+
201
+之前我们说,操作符只能被用于语言预定义的类型或底层类型为语言预定义类型的类型之间,但是有两个例外,这两个例外就是`==`和`!=`。
202
+
203
+这两个操作符允许被用在struct、array和接口类型之间。但是我们无法定义一个约束,用来包含这些所有可以进行比较的类型。
204
+
205
+## Comparable types in constraints
206
+
207
+因此,设计了一个内置的约束:`comparable`。这个约束所包含的类型集为所有可以进行`==`和`!=`操作的类型。
208
+
209
+例如:
210
+
211
+.code codes/comparable.go /COMPARABLE EXAMPLE OMIT/,/ COMPARABLE EXAMPLE OMIT/
212
+
213
+
214
+## Comparable types in constraints
215
+
216
+comparable作为一个约束,它也可以被嵌入在其他约束内。
217
+
218
+.code codes/comparable.go /ComparableHasher OMIT/,/ComparableHasher OMIT/
219
+
220
+满足上面约束的类型必须满足两个条件:
221
+- 必须可以比较
222
+- 必须有Hash() uintptr方法
223
+
224
+
225
+
226
+## Mutually referencing type parameters(相互引用类型参数)
227
+
228
+在一个泛型函数的定义中,类型参数之间可能会相互引用。
229
+
230
+例如,要实现图数据结构的一些基本算法。图包括两种结构,`Node`和`Edge`,`Node`应该有个方法Egdes()[]Edge,用于返回和节点相连的边。Edge也有个方法
231
+Nodes()(Node,Node),用于返回一条边两端连接的节点。一个完整的图就可以用[]Node来表示。
232
+
233
+
234
+## Mutually referencing type parameters
235
+
236
+代码如下:
237
+
238
+.code codes/graph.go /GRAPH OMIT/,/GRAPH OMIT/