Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: extend interface for generic #37594

Closed
awesee opened this issue Mar 2, 2020 · 10 comments
Closed

proposal: Go 2: extend interface for generic #37594

awesee opened this issue Mar 2, 2020 · 10 comments
Labels
FrozenDueToAge generics Issue is related to generics LanguageChange Proposal v2 A language change or incompatible library change WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Milestone

Comments

@awesee
Copy link
Contributor

awesee commented Mar 2, 2020

This proposal is motivated by go2draft-contracts.

interface may list explicit types that may be used as type arguments.

math.Max function
before

func MaxInt(x, y int) int {
	if x > y {
		return x
	}
	return y
}

func MaxInt64(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}

after

type Numeric interface {
	int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
}
// x, y has the same type
// func Max(type Numeric)(x, y Numeric) Numeric
func Max(x, y Numeric) Numeric {
	if x > y {
		return x
	}
	return y
}

type Numeric1 Numeric
// x, y may be different types
// func F(type Numeric, Numeric1)(x Numeric, y Numeric1)
func F(x Numeric, y Numeric1) {
	...
}

type Numeric1 Numeric
// x, y may be different types
// y, z has the same type
// func F(type Numeric, Numeric1)(x Numeric, y, z Numeric1)
func F(x Numeric, y, z Numeric1) {
	...
}
old => new
MaxInt = Max(int)
MaxInt64 = Max(int64)

return value type rely on input type

type T interface {
	int, uint, float64
}
type T1 T
type T2 T

// VALID
// func (type T)(x T)
func (x T)
// func (type T)(x T) int
func (x T) int
// func (type T)(x T) []T
func (x T) []T

// x, y has the same type
// func (type T)(x, y T) []T
func (x, y T) []T
// x, y may be different types
// func (type T1, T2)(x T1, y T2) []string
func (x T1, y T2) []string
// func (type T1, T2)(x T1, y T2) []T1
func (x T1, y T2) []T1
// func (type T1, T2)(x T1, y T2) []T2
func (x T1, y T2) []T2

method as default method

type myStr string
func (s myStr) String() string {
	return string(s)
}

type Stringer interface {
	int, float64, myStr
        String() string
}

// String() as default method
// func Stringer.String(type Stringer)(s Stringer) string
func (s Stringer) String() string {
	return fmt.Sprint(s)
}

// func Format(type Stringer)(s Stringer) string
func Format(s Stringer) string {
	return s.String()
}

var i int = 10
// use Stringer.String(int)(s int)
Format(i) // return "10"

var s myStr = "hello world"
// use myStr.String()
Format(s) // return "hello world"

Type assertions

type T interface {
	int, uint, float64
}

var v T = 123
t, ok := v.(float32) // always false
t, ok := v.(int) // true
t, ok := v.(uint) // false
t, ok := v.(float64) // false

var v T = 1.23
t, ok := v.(float32) // always false
t, ok := v.(int) // false
t, ok := v.(uint) // false
t, ok := v.(float64) // true

var v T(float64) = 123
t, ok := v.(float32) // always false
t, ok := v.(int) // false
t, ok := v.(uint) // false
t, ok := v.(float64) // true

type T3 []T 
// T3(int) = []int
// T3(uint) = []uint
// T3(float64) = []float64
var v T3(int) 
@gopherbot gopherbot added this to the Proposal milestone Mar 2, 2020
@ianlancetaylor ianlancetaylor changed the title proposal: Go 1: extend interface for generic proposal: Go 2: extend interface for generic Mar 2, 2020
@ianlancetaylor ianlancetaylor added generics Issue is related to generics LanguageChange v2 A language change or incompatible library change labels Mar 2, 2020
@ianlancetaylor
Copy link
Contributor

When you write

// x, y has the same type
func Max(x, y Numeric) Numeric {

are you saying that x and y have the same dynamic type? Because interface types don't work that way. The language has no way to require that two different interface values have the same dynamic type. Are you also adding that requirement in some way? If so, how?

@ianlancetaylor
Copy link
Contributor

For language change proposals, please fill out the template at https://go.googlesource.com/proposal/+/refs/heads/master/go2-language-changes.md .

When you are done, please reply to the issue with @gopherbot please remove label WaitingForInfo.

Thanks!

@gopherbot gopherbot added the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Mar 2, 2020
@awesee
Copy link
Contributor Author

awesee commented Mar 2, 2020

@ianlancetaylor

When you write

// x, y has the same type
func Max(x, y Numeric) Numeric {

are you saying that x and y have the same dynamic type? Because interface types don't work that way. The language has no way to require that two different interface values have the same dynamic type. Are you also adding that requirement in some way? If so, how?

contract Numeric(T) {
	T int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
}

func Max(type T Numeric)(x , y T) T {
	...
}
Max(int)(x, y) 
// if type inference is possible, can be write 
Max(x, y)

Similar with Contracts

type T interface {
	int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
}

func Max(type T)(x, y T) T {
	...
}
// for short can be write
func Max(x, y T) T {
	...
}

@ianlancetaylor
Copy link
Contributor

No, interfaces and type parameters are very different.

When I write

func F(x, y interface{})

then x and y can have different dynamic types.

When I write

func F(type T)(x, y T)

then x and y must have exactly the same type.

@awesee
Copy link
Contributor Author

awesee commented Mar 2, 2020

Got it. I think contracts is very good, just syntax is complex.

// contracts - syntax like function,but body like struct
// func functionName (Parameters) 
contract Numeric(T1, T2) {
	T1 int, int32 // anotherContract(T1)
	T2 float32, float64
}

contract Numeric1(T1, T3) {
	T1 int, int32 // anotherContract(T1)
	T3 uint, uint32
}

type SomeType(type T1, T2 Numeric) map[T1]T2
func (a SomeType(T1, T2)) M() SomeType(T1, T2) 

func(type T1, T2 Numeric)(x T1, y T2)
func(type T1, T3 Numeric1)(x T1, y T3)

How about as a new type, seem more flexible.

type T1 contract {
	int, int32
}
type T2 contract {
	float32, float64
}
type T3 contract {
	uint, uint32
}

type SomeType(type T1, T2) map[T1]T2
func (a SomeType(type T1, T2)) M() SomeType(type T1, T2)
// short can be write
type SomeType map[T1]T2
func (a SomeType) M() SomeType

func(type T1, T2)(x T1, y T2)
func(type T1, T3)(x T1, y T3)
// short can be write
func(x T1, y T2)
func(x T1, y T3)

@gocs
Copy link

gocs commented Mar 11, 2020

I like the idea that any primitive can be an interface.
Since Interfaces are set of methods, and int for example could implement arithmetic (+, -, *, /) , comparable (>, <, >=, <=, !=, ==), and basically, any thing that can perform/do operations implements something. Even empty struct implements something.
This would mean anything was an interface and would form as an "Interface-oriented",

So, the following could be possible, and for me I'm not sure about type parameter, but the interface type could just be fine:

type int interface {
	Arithmetic // interface to operators
	Comparable // interface to operators
	Stringer // interface to operators
	// etc..
}
...
type T interface {
	int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64
}

func Max(x, y T) T {
	...
}

@ianlancetaylor
Copy link
Contributor

The "etc." leaves out a lot when you start to think about slices, maps, channels, type conversions, untyped constants, slice expressions, index expressions, range loops.

@deanveloper
Copy link

@gocs If I'm not mistaken, what you say falls under the same pitfalls as the original proposal.

For instance, in the following code:

type T interface { int64, float64 }

func Max(x, y T) T {
    // ...
}

func main() {
    var i int64 = 9007199254740993
    var f float64 = math.MaxFloat64

    m := Max(i, f)
    a := Max(i, i)
    b := Max(f, f)
}

What are the types of m, a, and b? One might say that a is int64 and b is float64, but then what about m? One might try to say "then maybe all three are type T, although that means that in order to get back to int64 or float64, you would need a type conversion for trivial operations: strconv.FormatInt(int64(Max(5, 6)), 10) instead of the much more readable strconv.FormatInt(Max(5, 6), 10).

@gocs
Copy link

gocs commented Mar 28, 2020

Hi @deanveloper,

What are the types of m, a, and b?

The types of m, a, and b is T because that is what the function returns.

you would need a type conversion for trivial operations: strconv.FormatInt(int64(Max(5, 6)), 10)

If you would want to format string from int, the formal definition of func FormatInt(i int64, base int) string would be changed into type Num interface { int, int64, float64, ... }; func Format(n Num, base int) string and use something like strconv.Format(Max(5, 6), 10).

type T interface {
    // primitive type embeddings doesn't break compatibility because interfaces are implemented implicitly and already is.
    int // implements greater than operator and others implicitly
    int64 // implements greater than operator and others implicitly
    float64 // implements greater than operator and others implicitly
}

func Max(a, b T) T {
    if a > b { // both variables are comparable because int, int64, and float64 are comparable
        return a
    }
    return b
}

i := Max(1, 1.1)
s := strconv.Format(i, 10)

@gocs
Copy link

gocs commented Mar 28, 2020

I'm aware that there has to be a type between a struct and an interface, and it does not have to be a "Generics".

  • structs is too static
  • interface is too dynamic
  • structs can't be in an interface
  • interfaces can't have methods

IMO, this is my check list for a contract-like proposal:

  • contracts should be able to embed structs, interfaces, and another contract
  • contracts should be orthographic like structs and interfaces
  • contracts should be just a statement and generated on compile-time
  • contracts should precede all the types including method receiver

also, only thing holding me back is func (a A) B(type C, D)(e E, f F)(g G, h H) { return } might be too much. maybe put the type parameter out of the function declaration from another proposal

@awesee awesee closed this as completed Apr 3, 2020
@golang golang locked and limited conversation to collaborators May 31, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge generics Issue is related to generics LanguageChange Proposal v2 A language change or incompatible library change WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided.
Projects
None yet
Development

No branches or pull requests

5 participants