[generics] Parenthesized type parameters lead to obscure errors and difficult to read code

95 views
Skip to first unread message

firel...@gmail.com

unread,
Jun 22, 2020, 8:19:37 PM6/22/20
to golang-nuts
Parenthesized type parameters make code harder to read, require unpleasant workarounds to compile correctly, and return obscure errors if you do it wrong. The unpleasant workaround part may be rendered unnecessary if this suggestion in the draft design is implemented: that "x(T)" in a parameter list will always be interpreted as "<parametric type>(<type parameter>)" and not as "<variable name>(<parenthesized type name>)".

I am using https://gitlab.com/firelizzard/go-iter for my examples.

The following is from iter.go2, which defines types for generic iterators and iterables:

type Iterator(type T) interface{
        Next() (T, bool)
}

type Iterable(type T) interface{
        Iterate() (_ Iterator(T))
}

type Transform(type A, B) interface {
        Apply(_ Iterator(A)) (_ Iterator(B))
}

This works as expected - `go tool go2go test` succeeds. However, `(_ Iterator(T))` is less than ideal. I created a branch, parenthesis-problems, that reads a bit better:

type Iterator(type T) interface{
        Next() (T, bool)
}

type Iterable(type T) interface{
        Iterate() Iterator(T)
}

type Transform(type A, B) interface {
        Apply(Iterator(A)) Iterator(B)
}

However, this change leads to errors. `go tool go2go test` now fails:

type checking failed for iter
from.go2:24:9: cannot use TransformFunc(A, B)(tr) (value of type TransformFunc(A, B)) as Transform(A, B) value in return statement: wrong type for method Apply (have func(it iter.Iterator(A)) iter.Iterator(B), want func(Iterator A) iter.Iterator(B))
main_test.go2:44:29: cannot use it (variable of type Iterator(int)) as int value in argument
transform.go2:3:60: cannot use MapTransform(A, B)(tr) (value of type MapTransform(A, B)) as Transform(A, B) value in return statement: wrong type for method Apply (have func(it iter.Iterator(A)) iter.Iterator(B), want func(Iterator A) iter.Iterator(B))
transform.go2:4:63: cannot use FilterTransform(A)(tr) (value of type FilterTransform(A)) as Transform(A, A) value in return statement: wrong type for method Apply (have func(it iter.Iterator(A)) iter.Iterator(A), want func(Iterator A) iter.Iterator(A))
transform.go2:5:51: cannot use TakeTransform(A)(n) (value of type TakeTransform(A)) as Transform(A, A) value in return statement: wrong type for method Apply (have func(it iter.Iterator(A)) iter.Iterator(A), want func(Iterator A) iter.Iterator(A))
transform.go2:6:51: cannot use SkipTransform(A)(n) (value of type SkipTransform(A)) as Transform(A, A) value in return statement: wrong type for method Apply (have func(it iter.Iterator(A)) iter.Iterator(A), want func(Iterator A) iter.Iterator(A))
transform.go2:10:19: cannot use it.Iterate() (value of type Iterator(A)) as A value in argument

It is not immediately clear from these messages what the issue is.

If the suggestion to prefer "<parametric type>(<type parameter>)" over "<variable name>(<parenthesized type name>)" as an interpretation of "x(Y)", I think this issue may be resolved. But that still doesn't solve the "Lots of Irritating Silly Parentheses" issue. Here is an especially noisome example from map.go2:

func ToMap(type K comparable, V interface{})(it Iterator(KVP(K, V))) MapIterable(K, V) {
	m := map[K]V{}
	Each(it, func(_ int, kvp KVP(K, V)) error {
		m[kvp.Key] = kvp.Value
		return nil
	})
	return m
}


Reply all
Reply to author
Forward
0 new messages