From a117a71648cf9a05467c78be973d7c7693793806 Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 25 Sep 2018 22:01:07 +0200 Subject: [PATCH 01/23] contract as type + unify contract+interface This is my first attempt to unify contract and interfaces. I think it gives backward compatability, and unifies contracts and interfaces into one thing. The difference in boxing or not is expressed in how contracts are used. When applied to parameters, no boxing. When a contract is unary and is applied as a type, boxing, like interfaces now. --- design/go2draft-contracts.md | 290 ++++++++++++++++++++++++----------- 1 file changed, 197 insertions(+), 93 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index cdc04504..8119e3f6 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -2,19 +2,25 @@ Ian Lance Taylor\ Robert Griesemer\ + August 27, 2018 +Modified by Scott Cotton +Sep 24, 2018 + ## Abstract We suggest extending the Go language to add optional type parameters to types and functions. Type parameters may be constrained by contracts: they may be used as ordinary types that only support the operations described by the -contracts. +contracts. Contracts are thus extensions of interfaces which allow +specifying more than method names. + Type inference via a unification algorithm is supported to permit omitting type arguments from function calls in many cases. -Depending on a detail, the design can be fully backward compatible -with Go 1. +The design can be fully backward compatible with Go 1. + For more context, see the [generics problem overview](go2draft-generics-overview.md). @@ -54,7 +60,18 @@ similarities but are not always the same. ## Design -We will describe the complete design in stages based on examples. +We will describe the complete design by + +1. First, supposing that Go has no interface types. +1. Second we will build a complete design by means of +examples and use of a new construct called _contracts_. +This will involve defining a new keyword, `contract`. +1. We will then replace the keyword `contract` with `interface`, +and define Go's current interfaces as syntactic sugar on +top of contracts. + +The final steps gives backward compatibility, and unifies +the notion of contracts and interfaces. ### Type parameters @@ -113,6 +130,7 @@ At the call site, the `type` keyword is not used. Print(int)([]int{1, 2, 3}) ``` + ### Type contracts Let’s make our example slightly more complicated. @@ -176,9 +194,8 @@ contract between the generic code and calling code. ### Contract syntax -In this design, a contract has the same general form as a function. -The function body is never executed. -Instead, it describes, by example, a set of types. +In this design, a contract body has the same general form as a function. +The body is never executed. Instead, it describes, a set of types. For the `Stringify` example, we need to write a contract that says that the type has a `String` method that takes no arguments and @@ -186,14 +203,30 @@ returns a value of type `string`. Here is one way to write that: ```Go -contract stringer(x T) { - var s string = x.String() +type stringer(x T) contract { + var s string = x.String() +} +``` + +The above mechanism describes the type by example. It can be used with arbitrariry +language constructs, such as giving examples of indexing with square brackets, +testing for equality, assignability, addressability, dereferencing pointers, etc. + +Contract bodies have one special syntax in addition to the description of the +types by means of examples. In particular, A contract body may contain a type +parameter, such as `x` in the example above, following by a colon `:`, followed +by a list of method signatures as in Go's interfaces today. + +For example, one may write + +```Go +type stringer(x T) contract { + x: { + String() string + } } ``` -A contract is introduced with a new keyword `contract`. -The definition of a contract looks like the definition of a function, -except that the parameter types must be simple identifiers. ### Using a contract to verify type arguments @@ -208,9 +241,7 @@ error: the call is using types that the function’s contract does not permit. To validate the type arguments, each of the contract’s parameter types -is replaced with the corresponding type argument (there must be -exactly as many type arguments as there are parameter types; contracts -may not be variadic). +is replaced with the corresponding type argument The body of the contract is then type checked as though it were an ordinary function. If the type checking succeeds, the type arguments are valid. @@ -284,6 +315,54 @@ has type parameters; when validating the contract, the type parameters are passed to the function in the order in which they appear in the function definition. +#### Using a contract as a type +The Stringify example above shows a critical difference between contracts +and interfaces. Today one may define a variation of Stringify above without +contracts. +```Go +interface Stringer { + String() string +} /* builtin */ +func Stringify(s []Stringer) (ret []string) { + for _, v := range s { + ret = append(re, v.String()) + } + return ret +} +``` + +But this is in fact different. Notably, each element of s may have a different +type and thus must be boxed and the method call to String() must be looked up +for each element. In the contracts version, each element in s is of one +concrete type. + +In many situations one of the two behaviors is desireable. + +To achieve _variadic_ type instantiation of a contract, we use can +use it directly as a type + +```Go +func Stringify(s []stringer) (ret []string) { + for _, v := range s { + ret = append(ret, v.String()) // now valid + } + return ret +} +``` + +This has the effect of instructing the compiler to box type parameters +like `T` in the example above in the same way interfaces are currently +implemented. + +This only works when the contract has one type parameter. When a contract +has more than one type parameter, it is no longer really a type but rather +a relation between types. + + + + + + ### Contract syntactic details Before we continue, let’s cover a few details of the contract syntax. @@ -298,7 +377,7 @@ For example, this simple contract says that a value of type `From` may be converted to the type `To`. ```Go -contract convertible(_ To, f From) { +type convertible(_ To, f From) contract { To(f) } ``` @@ -348,7 +427,7 @@ from other packages, permitting a contract to easily say things like "this type must support the `io.Reader` interface:" ```Go -contract Readable(r T) { +type Readable(r T) contract { io.Reader(r) } ``` @@ -368,36 +447,6 @@ Of course, it is completely pointless to use a `goto` statement, or a `break`, `continue`, or `fallthrough` statement, in a contract body, as these statements do not say anything about the type arguments. -#### The contract keyword - -Contracts may only appear at the top level of a package. - -While contracts could be defined to work within the body of a -function, it’s hard to think of realistic examples in which they would -be useful. -We see this as similar to the way that methods can not be defined -within the body of a function. -A minor point is that only permitting contracts at the top level -permits the design to be Go 1 compatible. - -There are a few ways to handle the syntax: - -* We could make `contract` be a keyword only at the start of a - top-level declaration, and otherwise be a normal identifier. -* We could declare that if you use `contract` at the start of a - top-level declaration, then it becomes a keyword for the duration of - that package. -* We could make `contract` always be a keyword, albeit one that can - only appear in one place, in which case this design is not Go 1 - compatible. - -#### Exported contracts - -Like other top level declarations, a contract is exported if its name -starts with an upper-case letter. -An exported contract may be used by functions, types, or contracts in other -packages. - ### Multiple type parameters Although the examples shown so far only use a single type parameter, @@ -417,11 +466,11 @@ In `Print2` `s1` and `s2` may be slices of different types. In `Print2Same` `s1` and `s2` must be slices of the same element type. -Although functions may have multiple type parameters, they may only +Although functions may have multiple type parameters, they may only have a single contract. ```Go -contract viaStrings(t To, f From) { +type viaStrings(t To, f From) contract { var x string = f.String() t.Set(string("")) // could also use t.Set(x) } @@ -435,10 +484,37 @@ func SetViaStrings(type To, From viaStrings)(s []From) []To { } ``` +Note however, since unary contracts are types, one may have contracts +as types in addition to as type parameters. An example follows. + +```Go +type stringer(x T) contract { + x: { + String() string + } +} +type viaStrings(t To, f From) contract { + var x string = f.String() + t.Set(string("")) // could also use t.Set(x) +} + +func SetViaStringsWith(type To, From viaStrings)(s []From, with stringer) []To { + r := make([]To, len(s)) + for i, v := range s { + r[i].Set(fmt.Sprintf("%s-%s", v.String(), with)) + } + return r +} +``` + +The restriction for a single contract thus only applies to the type parameters. + ### Parameterized types -We want more than just generic functions: we also want generic types. -We suggest that types be extended to take type parameters. +The contracts mechanism above provides one way of creating parameterized types +and generic functions. But we want generic types for other type expressions as +well. We suggest that other type expressions be extended to take type +parameters as well ```Go type Vector(type Element) []Element @@ -458,6 +534,16 @@ This is called _instantiation_. var v Vector(int) ``` + +The same works with contracts +```Go +type C(t T) contract { + t + t +} +var v C(int) +``` + + Parameterized types can have methods. The receiver type of a method must list the type parameters. They are listed without the `type` keyword or any contract. @@ -478,6 +564,7 @@ type List(type Element) struct { val Element } + // This is INVALID. type P(type Element1, Element2) struct { F *P(Element2, Element1) // INVALID; must be (Element1, Element2) @@ -558,8 +645,8 @@ contract call. This contract embeds the contract `stringer` defined earlier. ```Go -contract PrintStringer(x X) { - stringer(X) +type PrintStringer(x X) contract { + stringer(x) x.Print() } ``` @@ -567,7 +654,7 @@ contract PrintStringer(x X) { This is roughly equivalent to ```Go -contract PrintStringer(x X) { +type PrintStringer(x X) contract { var s string = x.String() x.Print() } @@ -587,7 +674,7 @@ package comparable // The equal contract describes types that have an Equal method for // the same type. -contract equal(v T) { +type equal contract(v T) { // All that matters is type checking, so reusing v as the argument // means that the type argument must have a Equal method such that // the type argument itself is assignable to the Equal method’s @@ -648,7 +735,7 @@ like finding the shortest path. ```Go package graph -contract G(n Node, e Edge) { +type G(n Node, e Edge) contract { var _ []Edge = n.Edges() var from, to Node = e.Nodes() } @@ -682,25 +769,12 @@ There are no interface types here, but we can instantiate var g = graph.New(*Vertex, *FromTo)([]*Vertex{ ... }) ``` -`*Vertex` and `*FromTo` are not interface types, but when used -together they define methods that implement the contract `graph.G`. -Because of the way that the contract is written, we could also use the -non-pointer types `Vertex` and `FromTo`; the contract implies that the -function body will always be able to take the address of the argument -if necessary, and so will always be able to call the pointer method. - -Although `Node` and `Edge` do not have to be instantiated with -interface types, it is also OK to use interface types if you like. - -```Go -type NodeInterface interface { Edges() []EdgeInterface } -type EdgeInterface interface { Nodes() (NodeInterface, NodeInterface) } -``` - -We could instantiate `graph.Graph` with the types `NodeInterface` and -`EdgeInterface`, since they implement the `graph.G` contract. -There isn’t much reason to instantiate a type this way, but it is -permitted. +When `*Vertex` and `*FromTo` are used together they define methods that +implement the contract `graph.G`. Because of the way that the contract is +written, we could also use the non-pointer types `Vertex` and `FromTo`; the +contract implies that the function body will always be able to take the address +of the argument if necessary, and so will always be able to call the pointer +method. This ability for type parameters to refer to other type parameters illustrates an important point: it should be a requirement for any @@ -711,13 +785,10 @@ ways that the compiler can check. ### Values of type parameters are not boxed In the current implementations of Go, interface values always hold -pointers. -Putting a non-pointer value in an interface variable causes the value -to be _boxed_. -That means that the actual value is stored somewhere else, on the heap -or stack, and the interface value holds a pointer to that location. +pointers. Recall that in this design, there are no interfaces to consider +as of yet, we have assumed temporarily that Go has no interfaces. -In contrast to interface values, values of instantiated polymorphic types are not boxed. +Values of instantiated polymorphic types are not boxed. For example, let’s consider a function that works for any type `T` with a `Set(string)` method that initializes the value based on a string, and uses it to convert a slice of `string` to a slice of `T`. @@ -725,7 +796,7 @@ string, and uses it to convert a slice of `string` to a slice of `T`. ```Go package from -contract setter(x T) { +type setter(x T) contract { var _ error = x.Set(string) } @@ -943,6 +1014,8 @@ var V = Pair{1, 2} // inferred as Pair(int){1, 2} It’s not clear how often this will arise in real code.) + + ### Instantiating a function Go normally permits you to refer to a function without passing any @@ -979,7 +1052,7 @@ For example, this code is permitted even if it is called with a type argument that is not an interface type. ```Go -contract byteReader(x T) { +type byteReader(x T) contract { // This expression says that x is convertible to io.Reader, or, // in other words, that x has a method Read([]byte) (int, error). io.Reader(x) @@ -1092,8 +1165,9 @@ simple assignment statement like the ones shown above. field with function type. When a contract needs to describe one of these cases, it can use a -type conversion to an interface type. -The interface type permits the method to be precisely described. +type conversion to an interface type, or it can specify them using +the type method list syntax. +Either syntax permits the method to be precisely described. If the conversion to the interface type passes the type checker, then the type argument must have a method of that exact type. @@ -1108,7 +1182,7 @@ However, it is possible to write a function body that can only call a value method, not a pointer method. For example: ```Go -contract adjustable(x T) { +type adjustable(x T) contract { var _ T = x.Adjust() x.Apply() } @@ -1129,7 +1203,7 @@ method. That can be done like this: ```Go -contract adjustable(x T) { +type adjustable(x T) contract { var _ T = x.Adjust() var f func() T f().Apply() @@ -1140,6 +1214,8 @@ The rule is that if the contract body contains a method call on a non-addressable value, then the function body may call the method on a non-addressable value. +NB(wsc): this is a wart. + #### Operators Method calls are not sufficient for everything we want to express. @@ -1665,6 +1741,7 @@ the type arguments from the regular arguments. The current design seems to be the best, but perhaps something better is possible. + ##### What does := mean in a contract body? If a contract body uses a short declaration, such as @@ -1681,6 +1758,9 @@ It’s less clear what it permits in the function using this contract. For example, does it permit the function to call the `String` method and assign the result to a variable of empty interface type? +TBD(wsc): it says that there is a return type to the method String + + ##### Pointer vs. value methods in contracts It seems that the natural ways to write a contract calling for certain @@ -1763,6 +1843,9 @@ When parsing a type declaration `type A [T] int` it’s ambiguous whether this is a parameterized type defined (uselessly) as `int` or whether it is an array type with `T` elements. +NB(wsc): it might be nice to use squre brackets for function type parameters +and parens for type type parameters. + ##### Why not use `F«T»`? We considered it but we couldn’t bring ourselves to require @@ -1827,7 +1910,7 @@ need for boilerplate definitions in order to use `sort.Sort`. With this design, we can add to the sort package as follows: ```Go -contract ordered(e Ele) { e < e } +type ordered(e Ele) contract { e < e } type orderedSlice(type Ele ordered) []Ele @@ -1967,7 +2050,7 @@ methods rather than operators like `[]`. // Package set implements sets of any type. package set -contract comparable(Ele) { Ele == Ele } +type comparable(Ele) contract { Ele == Ele } type Set(type Ele comparable) map[Ele]struct{} @@ -2324,11 +2407,11 @@ package metrics import "sync" -contract comparable(v T) { +type comparable(v T) contract { v == v } -contract cmp1(T) { +type cmp1(T) contract { comparable(T) // contract embedding } @@ -2346,7 +2429,7 @@ func (m *Metric1(T)) Add(v T) { m[v]++ } -contract cmp2(T1, T2) { +type cmp2(T1, T2) contract { comparable(T1) comparable(T2) } @@ -2370,7 +2453,7 @@ func (m *Metric2(T1, T2)) Add(v1 T1, v2 T2) { m[key(T1, T2){v1, v2}]++ } -contract cmp3(T1, T2, T3) { +type cmp3(T1, T2, T3) contract { comparable(T1) comparable(T2) comparable(T3) @@ -2559,3 +2642,24 @@ var ServerContextKey = context.NewKey(*Server)("http_server") Code that uses `Key.WithValue` and `Key.Value` instead of `context.WithValue` and `context.Value` does not need any type assertions and is compile-time type-safe. + +## Back to interfaces +Now suppose we +1. Replace the keyword 'contract' in this design; and +1. Make the case of +```Go +type X interface { + // ... as per Go 1 +} +``` +syntactic sugar for +```Go +type X(x T) interface { + x: { + // ... as per Go 1 + } +} +``` + +Then we have unified types and interfaces and made it backward compatible syntactically (I think). + From 94214a73494a27a8704df7326c9ecec9f36b74be Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 25 Sep 2018 22:07:10 +0200 Subject: [PATCH 02/23] some re-formatting --- design/go2draft-contracts.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 8119e3f6..638a3bac 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -14,7 +14,8 @@ We suggest extending the Go language to add optional type parameters to types and functions. Type parameters may be constrained by contracts: they may be used as ordinary types that only support the operations described by the -contracts. Contracts are thus extensions of interfaces which allow +contracts. +Contracts are thus extensions of interfaces which allow specifying more than method names. Type inference via a unification algorithm is supported to permit @@ -317,7 +318,8 @@ function definition. #### Using a contract as a type The Stringify example above shows a critical difference between contracts -and interfaces. Today one may define a variation of Stringify above without +and interfaces. +Today one may define a variation of Stringify above without contracts. ```Go interface Stringer { @@ -331,9 +333,11 @@ func Stringify(s []Stringer) (ret []string) { } ``` -But this is in fact different. Notably, each element of s may have a different +But this is in fact different. +Notably, each element of s may have a different type and thus must be boxed and the method call to String() must be looked up -for each element. In the contracts version, each element in s is of one +for each element. +In the contracts version, each element in s is of one concrete type. In many situations one of the two behaviors is desireable. @@ -354,7 +358,8 @@ This has the effect of instructing the compiler to box type parameters like `T` in the example above in the same way interfaces are currently implemented. -This only works when the contract has one type parameter. When a contract +This only works when the contract has one type parameter. +When a contract has more than one type parameter, it is no longer really a type but rather a relation between types. @@ -512,8 +517,10 @@ The restriction for a single contract thus only applies to the type parameters. ### Parameterized types The contracts mechanism above provides one way of creating parameterized types -and generic functions. But we want generic types for other type expressions as -well. We suggest that other type expressions be extended to take type +and generic functions. +But we want generic types for other type expressions as +well. +We suggest that other type expressions be extended to take type parameters as well ```Go From 81a735eb717a95b7f049cf04e6217deeabcb938b Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 25 Sep 2018 23:34:59 +0200 Subject: [PATCH 03/23] clarify overal design section --- design/go2draft-contracts.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 638a3bac..18985d30 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -69,7 +69,7 @@ examples and use of a new construct called _contracts_. This will involve defining a new keyword, `contract`. 1. We will then replace the keyword `contract` with `interface`, and define Go's current interfaces as syntactic sugar on -top of contracts. +top of contracts which use the 'interface' keyword. The final steps gives backward compatibility, and unifies the notion of contracts and interfaces. @@ -195,8 +195,10 @@ contract between the generic code and calling code. ### Contract syntax -In this design, a contract body has the same general form as a function. -The body is never executed. Instead, it describes, a set of types. +In this design, a contract body has the same general form as a function, +with one exception. +The body is never executed. +Instead, it describes, a set of types. For the `Stringify` example, we need to write a contract that says that the type has a `String` method that takes no arguments and @@ -209,14 +211,16 @@ type stringer(x T) contract { } ``` -The above mechanism describes the type by example. It can be used with arbitrariry +The above mechanism describes the type by example. +It can be used with arbitrariry language constructs, such as giving examples of indexing with square brackets, testing for equality, assignability, addressability, dereferencing pointers, etc. Contract bodies have one special syntax in addition to the description of the -types by means of examples. In particular, A contract body may contain a type +types by means of examples. +In particular, a contract body may contain a type parameter, such as `x` in the example above, following by a colon `:`, followed -by a list of method signatures as in Go's interfaces today. +by a bracketed list of method signatures as in Go's interfaces today. For example, one may write @@ -336,11 +340,12 @@ func Stringify(s []Stringer) (ret []string) { But this is in fact different. Notably, each element of s may have a different type and thus must be boxed and the method call to String() must be looked up -for each element. +for each element. We call this _variadic_ type instantiation. In the contracts version, each element in s is of one concrete type. -In many situations one of the two behaviors is desireable. +In many situations one of the two behaviors is desireable, in some +certainly a mix of the two behaviors is desireable. To achieve _variadic_ type instantiation of a contract, we use can use it directly as a type @@ -364,10 +369,6 @@ has more than one type parameter, it is no longer really a type but rather a relation between types. - - - - ### Contract syntactic details Before we continue, let’s cover a few details of the contract syntax. @@ -2652,8 +2653,8 @@ assertions and is compile-time type-safe. ## Back to interfaces Now suppose we -1. Replace the keyword 'contract' in this design; and -1. Make the case of +1. First replace the keyword 'contract' in this design with 'interface' and +1. Then make the case of ```Go type X interface { // ... as per Go 1 From b27d7c6b7b697d6db1327e86aedce75e43458e77 Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 25 Sep 2018 23:45:55 +0200 Subject: [PATCH 04/23] more cleanup remove question about why not interfaces, no longer relevant. --- design/go2draft-contracts.md | 41 ++++++++++++------------------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 18985d30..3b1e48bc 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -195,8 +195,8 @@ contract between the generic code and calling code. ### Contract syntax -In this design, a contract body has the same general form as a function, -with one exception. +In this design, a contract is a type declaration whose body has the same general +form as a function, with one exception. The body is never executed. Instead, it describes, a set of types. @@ -218,9 +218,9 @@ testing for equality, assignability, addressability, dereferencing pointers, etc Contract bodies have one special syntax in addition to the description of the types by means of examples. -In particular, a contract body may contain a type -parameter, such as `x` in the example above, following by a colon `:`, followed -by a bracketed list of method signatures as in Go's interfaces today. +In particular, a contract body may contain an example parameter, such as `x` in +above, following by a colon `:`, followed by a bracketed list of method +signatures as in Go's interfaces today. For example, one may write @@ -1462,11 +1462,11 @@ The contract body can use `var y = x.f` to describe the field’s type. ```Go package move -contract counter(x T) { +type counter(x T) contract { var _ int = x.Count } -contract counters(T1, T2) { // as with a func, parameter names may be omitted. +type counters(T1, T2) contract { // as with a func, parameter names may be omitted. // Use contract embedding to say that both types must have a // Count field of type int. counter(T1) @@ -1498,6 +1498,7 @@ exhaustive set of rules describing when a contract body cannot be satisfied. It may be appropriate to add a vet check for this, if possible. +NB(wsc): sounds like a SAT problem to me. ### Implementation @@ -1749,6 +1750,9 @@ the type arguments from the regular arguments. The current design seems to be the best, but perhaps something better is possible. +NB(wsc): suggest to use square brackets for function type params and parens for +type type params. + ##### What does := mean in a contract body? @@ -1805,24 +1809,6 @@ This section lists some of those ideas in the hopes that it will help to reduce repetitive discussion. The ideas are presented in the form of a FAQ. -##### Why not use interfaces instead of contracts? - -_The interface method syntax is familiar._ -_Writing contract bodies with `x + x` is ordinary Go syntax, but it_ -_is stylized, repetitive, and looks weird._ - -It is unclear how to represent operators using interface methods. -We considered syntaxes like `+(T, T) T`, but that is confusing and -repetitive. -Also, a minor point, but `==(T, T) bool` does not correspond to the -`==` operator, which returns an untyped boolean value, not `bool`. -We also considered writing simply `+` or `==`. -That seems to work but unfortunately the semicolon insertion rules -require writing a semicolon after each operator at the end of a line. -Using contracts that look like functions gives us a familiar syntax at -the cost of some repetition. -These are not fatal problems, but they are difficulties. - ##### Why not put type parameters on packages? We investigated this extensively. @@ -2654,7 +2640,7 @@ assertions and is compile-time type-safe. ## Back to interfaces Now suppose we 1. First replace the keyword 'contract' in this design with 'interface' and -1. Then make the case of +1. Then make the case of Go1's interfaces ```Go type X interface { // ... as per Go 1 @@ -2669,5 +2655,6 @@ type X(x T) interface { } ``` -Then we have unified types and interfaces and made it backward compatible syntactically (I think). +After doing the above, we have unified types and interfaces and made it +backward compatible syntactically (I think). From efdc8133b44bc580c62800be3e5897c7e3516b29 Mon Sep 17 00:00:00 2001 From: Scott Date: Tue, 25 Sep 2018 23:57:45 +0200 Subject: [PATCH 05/23] some more syntax substs --- design/go2draft-contracts.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 3b1e48bc..d5af75e2 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -326,7 +326,7 @@ and interfaces. Today one may define a variation of Stringify above without contracts. ```Go -interface Stringer { +type Stringer interface { String() string } /* builtin */ func Stringify(s []Stringer) (ret []string) { @@ -1253,7 +1253,7 @@ not use `==` to compare values of slice, map, or function type). This is easy to address using a contract. ```Go -contract comparable(x T) { +type comparable(x T) contract { x == x } @@ -1307,7 +1307,7 @@ numeric types: ```Go package check -contract convert(t To, f From) { +type convert(t To, f From) contract { To(f) From(t) f == f @@ -1336,7 +1336,7 @@ This is most naturally written as an assignment from an untyped constant. ```Go -contract untyped(x T) { +type untyped(x T) contract { x = 0 } ``` @@ -1361,7 +1361,7 @@ If the contract did not say `x = 1000`, the expression `v + 1000` would be invalid. ```Go -contract add1K(x T) { +type add1K(x T) contract { x = 1000 x + x } From dbd507a9ba58c66f776b4fda8078fa0fe3017afa Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 00:05:53 +0200 Subject: [PATCH 06/23] update reflect --- design/go2draft-contracts.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index d5af75e2..c2893864 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1109,7 +1109,7 @@ This is only an issue when the type is instantiated in place. ### Reflection -We do not propose to change the reflect package in any way. +We do not propose to change the reflect package in any significant way When a type or function is instantiated, all of the type parameters will become ordinary non-generic types. The `String` method of a `reflect.Type` value of an instantiated type @@ -1120,6 +1120,16 @@ It’s impossible for non-generic code to refer to generic code without instantiating it, so there is no reflection information for uninstantiated generic types or functions. +However, all references to interfaces in package reflect would apply +to their new implementation in this design, which is essentially +unary contracts. As many ways exist to enforce existence of methods +by contracts, only those methods which are explicitly declared are +available via methods of reflect.Type. + +It may be useful to extend the contract body as a list of statements +using go syntax package. But this is out of scope for this stage of +this design. + ### Contracts details Let’s take a deeper look at contracts. From 537ceb01cdf8b8e12c402a3cd7a8cabb89962fbe Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 00:10:42 +0200 Subject: [PATCH 07/23] better phrase --- design/go2draft-contracts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index c2893864..11b5d6a9 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -198,7 +198,7 @@ contract between the generic code and calling code. In this design, a contract is a type declaration whose body has the same general form as a function, with one exception. The body is never executed. -Instead, it describes, a set of types. +Instead, it describes, a set of types by means of example usage. For the `Stringify` example, we need to write a contract that says that the type has a `String` method that takes no arguments and From d8bcb3c6e2a5044cdf72a059a368874fce75e97f Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 00:21:05 +0200 Subject: [PATCH 08/23] 2 functions of contracts +=unary type check --- design/go2draft-contracts.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 11b5d6a9..6acf14f5 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -236,7 +236,7 @@ type stringer(x T) contract { ### Using a contract to verify type arguments A contract serves two purposes. -First, contracts are used to validate a set of type arguments. +First, contracts are used to validate type arguments. As shown above, when a function with type parameters is called, it will be called with a set of type arguments. When the compiler sees the function call, it will use the contract to @@ -260,6 +260,32 @@ only type assignable to `string` is, in fact, `string`.) If any of those statements about the type argument are not true, the contract body will fail when it is type checked. +#### Unary contracts as types +Contracts which have only one type argument are unary. They thus +describe a restriction on a type. +As such, they may be used just +as ordinary types. +When they are used as ordinary types, the +the type argument is implicitly the type of the value which is asserted +to implement the contract. For example + +```Go +type stringer(x T) contract { + x: { + String() string + } +} +var er stringer +``` + +describes the variable `er` with type stringer. er is not a concrete value +but rather a boxed value that must conform to `stringer`. Unary contracts +which are applied as types also validates the (implicit) type argument of +the associated value. + + + + ### The party of the second part A contract is not used only at the call site. From b8a9933fe478336125853837eba0a617c4e574bb Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 00:23:19 +0200 Subject: [PATCH 09/23] typo fix --- design/go2draft-contracts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 6acf14f5..659cee38 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -385,8 +385,8 @@ func Stringify(s []stringer) (ret []string) { } ``` -This has the effect of instructing the compiler to box type parameters -like `T` in the example above in the same way interfaces are currently +This has the effect of instructing the compiler to box values +like `v` in the example above in the same way interfaces are currently implemented. This only works when the contract has one type parameter. From b8f5b3503127c5cf2d9bad981555f9c1e8e8073c Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 01:19:22 +0200 Subject: [PATCH 10/23] + unary example issue --- design/go2draft-contracts.md | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 659cee38..1b7a4944 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1806,8 +1806,6 @@ It’s less clear what it permits in the function using this contract. For example, does it permit the function to call the `String` method and assign the result to a variable of empty interface type? -TBD(wsc): it says that there is a return type to the method String - ##### Pointer vs. value methods in contracts @@ -1835,6 +1833,38 @@ will have only non-existent or trivial requirements on their type parameters. More experience will be needed to see whether this is a problem. +##### Unary contracts as types applied with example code + +If a unary contract such as +```Go +type addOp(xy T) contract { + var z int = x + y +} +``` + +is used as a type, for example +```Go +func F(x addOp) { + z := x + x +} +``` + +Then there is no method implementing the addition operator, since +x is boxed. + +Numerous discussions have expressed the idea of using builtin methods +which correspond to usage examples and operators, such as in Python's +`__add__` method. + +For the moment, this design only supports implementations of actual +Go methods, as in the existing Go1 interfaces. This is not a problem +of compatibility, but it is an unnatural restriction. +If and when defining some builtin operations via methods becomes possible, +then they may be dispatched with Go2 contracts as in this design. + + + + #### Discarded ideas This design is not perfect, and it will be changed as we gain From dd93ad0a32aa4803b71f8bc420cd390a0971205b Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 01:37:12 +0200 Subject: [PATCH 11/23] typo fix --- design/go2draft-contracts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 1b7a4944..23e49f61 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1837,8 +1837,8 @@ More experience will be needed to see whether this is a problem. If a unary contract such as ```Go -type addOp(xy T) contract { - var z int = x + y +type addOp(x T) contract { + var z int = x + x } ``` From 263036d741de0acd320e3bf9b59c65e45089e1fa Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 15:45:03 +0200 Subject: [PATCH 12/23] add reference to other feedback --- design/go2draft-contracts.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 23e49f61..8b7088dd 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1862,6 +1862,11 @@ of compatibility, but it is an unnatural restriction. If and when defining some builtin operations via methods becomes possible, then they may be dispatched with Go2 contracts as in this design. +This later has been proposed as part of the feedback +[here](https://gist.github.com/rogpeppe/0a87ef702189689201ef1d4a170939ac). + +TBD(wsc): recast above gist in terms of unified contracts and interfaces. + From 0b37c50a9cb82e51a203457944dec85eeba1f5da Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 15:51:08 +0200 Subject: [PATCH 13/23] another syntax substition from orig to mod --- design/go2draft-contracts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 8b7088dd..c71705d3 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1459,7 +1459,7 @@ unnecessary conversions to `[]byte` in order to call `append` and `copy`, but perhaps the compiler can eliminate those. ```Go -contract strseq(x T) { +type strseq(x T) contract { []byte(x) T([]byte{}) len(x) From 56d3a54e2584c21f528546b93596e960207bfea3 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 17:16:50 +0200 Subject: [PATCH 14/23] some more unification of commentary --- design/go2draft-contracts.md | 57 ++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index c71705d3..a3604f3c 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -445,7 +445,7 @@ a bit more convenient when we get to type inference. #### Restrictions on contract bodies -Although a contract looks like a function body, contracts may not +Although a contract body looks like a function body, contracts may not themselves have type parameters. Contracts may also not have result parameters, and it follows that they may not use a `return` statement to return values. @@ -456,11 +456,11 @@ This rule is intended to make it harder to accidentally change the meaning of a contract. As a compromise, a contract is permitted to refer to names imported from other packages, permitting a contract to easily say things like -"this type must support the `io.Reader` interface:" +"this type must support the `io.Reader` contract:" by embedding. ```Go type Readable(r T) contract { - io.Reader(r) + io.Reader(T) } ``` @@ -662,7 +662,7 @@ Where it would be useful to add type arguments to a method, people will have to write a top-level function. Making this decision avoids having to specify the details of exactly -when a method with type arguments implements an interface. +when a method with type arguments supports a contract. (This is a feature that can perhaps be added later if it proves necessary.) @@ -780,12 +780,12 @@ func (*Graph(Node, Edge)) ShortestPath(from, to Node) []Edge { ... } ``` At first glance it might be hard to see how this differs from similar -code using interface types. +code using boxed types/variadic type instantiation. The difference is that although `Node` and `Edge` have specific -methods, they are not interface types. +methods, they are not boxed. In order to use `graph.Graph`, the type arguments used for `Node` and `Edge` have to define methods that follow a certain pattern, but they -don’t have to actually use interface types to do so. +don’t have to actually use boxing/variadic instantiation to do so. For example, consider these type definitions in some other package: @@ -796,7 +796,7 @@ type FromTo struct { ... } type (ft *FromTo) Nodes() (*Vertex, *Vertex) { ... } ``` -There are no interface types here, but we can instantiate +There is no variadic types instantiatio here, but we can instantiate `graph.Graph` using the type arguments `*Vertex` and `*FromTo`: ```Go @@ -1073,7 +1073,7 @@ Sometimes, though, it’s possible to use a more efficient implementation for some type arguments. The language already has mechanisms for code to find out what type it is working with: type assertions and type switches. -Those are normally only permitted with interface types. +Those are normally, in current Go1, only permitted with interface types. In this design, functions are also permitted to use them with values whose types are type parameters, or based on type parameters. @@ -1083,7 +1083,8 @@ It’s merely occasionally convenient, and it may result in more efficient code. For example, this code is permitted even if it is called with a type -argument that is not an interface type. +argument that is not a variadic type instantiation/boxed type (fancy talk for +Go1 interface). ```Go type byteReader(x T) contract { @@ -1152,10 +1153,6 @@ unary contracts. As many ways exist to enforce existence of methods by contracts, only those methods which are explicitly declared are available via methods of reflect.Type. -It may be useful to extend the contract body as a list of statements -using go syntax package. But this is out of scope for this stage of -this design. - ### Contracts details Let’s take a deeper look at contracts. @@ -1208,15 +1205,12 @@ simple assignment statement like the ones shown above. * There is no way to distinguish a method call from a call of a struct field with function type. -When a contract needs to describe one of these cases, it can use a -type conversion to an interface type, or it can specify them using -the type method list syntax. -Either syntax permits the method to be precisely described. -If the conversion to the interface type passes the type checker, then -the type argument must have a method of that exact type. +When a contract needs to describe one of these cases, it can +specify them using the method list syntax, or via embedding +a contract which does this. -An explicit method call, or a conversion to an interface type, can not -be used to distinguish a pointer method from a value method. +When methods are listed, one cannot distinguish a pointer method from a value +method in example based code. When the function body calls a method on an addressable value, this doesn’t matter; since all value methods are part of the pointer type’s method set, an addressable value can call either pointer methods or @@ -1361,7 +1355,7 @@ func Convert(type To, From convert)(from From) To { Note that the contract needs to explicitly permit both converting `To` to `From` and converting `From` to `To`. The ability to convert one way doesn’t necessarily imply being able to -convert the other way; consider `check.Convert(int, interface{})(0, 0)`. +convert the other way; consider (in current Go1) `check.Convert(int, interface{})(0, 0)`. #### Untyped constants @@ -1864,8 +1858,10 @@ then they may be dispatched with Go2 contracts as in this design. This later has been proposed as part of the feedback [here](https://gist.github.com/rogpeppe/0a87ef702189689201ef1d4a170939ac). +also +[here](https://gist.github.com/pat42smith/ed63aca983d4ba14fdfa320296211f40). -TBD(wsc): recast above gist in terms of unified contracts and interfaces. +TBD(wsc): recast above gists in terms of unified contracts and interfaces. @@ -2249,7 +2245,7 @@ The important points are: * The code is written in a natural Go style, using the key and value types where needed. * The keys and values are stored directly in the nodes of the tree, - not using pointers and not boxed as interface values. + not using pointers and as not boxed values. ```Go // Package orderedmap provides an ordered map, implemented as a binary tree. @@ -2726,6 +2722,15 @@ type X(x T) interface { } ``` +Then we have anonymous interfaces to deal with as well +```Go +type anything interface{} +``` +this could similarly be rewritten to +```Go +type anything(x T) interface {} +``` + After doing the above, we have unified types and interfaces and made it -backward compatible syntactically (I think). +backward compatible (I think). From f5e8711f2238ed27665425ea910a9e5353e4c4c9 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 17:37:20 +0200 Subject: [PATCH 15/23] more operator gist references --- design/go2draft-contracts.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index a3604f3c..c4a9bb3d 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1860,6 +1860,8 @@ This later has been proposed as part of the feedback [here](https://gist.github.com/rogpeppe/0a87ef702189689201ef1d4a170939ac). also [here](https://gist.github.com/pat42smith/ed63aca983d4ba14fdfa320296211f40). +and +[here](https://gist.github.com/deanveloper/c495da6b9263b35f98b773e34bd41104) TBD(wsc): recast above gists in terms of unified contracts and interfaces. From 190935210f9291aecb039ccfcdcfc76d216611ac Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 20:38:25 +0200 Subject: [PATCH 16/23] clarify 1 contract for all type params in a fn --- design/go2draft-contracts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index c4a9bb3d..f13c87a8 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -498,8 +498,8 @@ In `Print2` `s1` and `s2` may be slices of different types. In `Print2Same` `s1` and `s2` must be slices of the same element type. -Although functions may have multiple type parameters, they may only -have a single contract. +Although functions may have multiple type parameters, these type parameter +may only have a single contract. ```Go type viaStrings(t To, f From) contract { From 14b02ee44f37943c3520a84940425ea2f6241ac8 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 20:41:44 +0200 Subject: [PATCH 17/23] graph example clarification --- design/go2draft-contracts.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index f13c87a8..19a0f80e 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -796,8 +796,8 @@ type FromTo struct { ... } type (ft *FromTo) Nodes() (*Vertex, *Vertex) { ... } ``` -There is no variadic types instantiatio here, but we can instantiate -`graph.Graph` using the type arguments `*Vertex` and `*FromTo`: +There is no boxing/variadic type instantiation here, but we can instantiate +`graph.Graph` statically using the type arguments `*Vertex` and `*FromTo`: ```Go var g = graph.New(*Vertex, *FromTo)([]*Vertex{ ... }) From df2d2c75a554fa75cea4447aa70851d0dcce015c Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 22:54:07 +0200 Subject: [PATCH 18/23] a tentative future way out of the biggest issue. --- design/go2draft-contracts.md | 91 ++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 3 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 19a0f80e..00c7331c 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1090,7 +1090,7 @@ Go1 interface). type byteReader(x T) contract { // This expression says that x is convertible to io.Reader, or, // in other words, that x has a method Read([]byte) (int, error). - io.Reader(x) + io.Reader(T) } func ReadByte(type T byteReader)(r T) (byte, error) { @@ -1854,7 +1854,7 @@ For the moment, this design only supports implementations of actual Go methods, as in the existing Go1 interfaces. This is not a problem of compatibility, but it is an unnatural restriction. If and when defining some builtin operations via methods becomes possible, -then they may be dispatched with Go2 contracts as in this design. +then they may be dispatched with Go2 contracts as in this design. This later has been proposed as part of the feedback [here](https://gist.github.com/rogpeppe/0a87ef702189689201ef1d4a170939ac). @@ -1863,9 +1863,94 @@ also and [here](https://gist.github.com/deanveloper/c495da6b9263b35f98b773e34bd41104) -TBD(wsc): recast above gists in terms of unified contracts and interfaces. +Note that in this design, the only problem is for finding an implementation +of the the boxed case/variadic instantiation, but that for operator +overloading the problem is more general and applies also to static type +parameter instantiation as well. + +One possible way to deal with this is to have a special package which +takes pragma commens, like "C" is a special package for cgo whose pragma +allow for #include's and the like, and then to use an approach similar to +that suggested +[here](https://gist.github.com/rogpeppe/0a87ef702189689201ef1d4a170939ac). + +For example: +```Go +// bind[+]: fix.Add +// bind[*]: fix.Mul +// bind[0]: Zero +// bind[1]: One +import _ "operator/bind" + +// Q 32.32 fixed point type +type fix uint64 +const ( + Zero fix = 0 + One = fix(1<<32) +) +func (f fix) Add(a, b fix) fix { + return a+b +} + +func (f fix) Mul(a, b fix) fix { + var res fix + // this one is more complicated, implementation omitted. + return res +} +``` + +This would give the compiler a compile time way of associating methods of +numeric types to operators. +It is also a little bit ugly, which might be +a good thing to help restrict its usage to appropriate places. +Note also that this example brings up the problem of the value for 1, which +might well reasonably vary for numeric implementations. +Such strange things that pop up with operator overloading motivate +removing it from the type system and placing it in special package. + +Supposing the above were in place, let us examine some examples. + +```Go +// a multiplication contract, it has a one. +type Multipliable(a, b, c T) contract { + var one T = 1 + c = a*b +} + +// static type instantiation. +func MulsStatic(type T Multipliable)(vals ...T) T { + var res T = 1 + for _, v := range vals { + res = res * v + } + return res +} + +// the variadic/dynamic/boxed case +func MulsBoxed(a, b, c Multipliable) Multipliable { + var res Multipliable = 1 + for _, v := range vals { + res = res * v + } + return res +} +``` + +The fixed point case is an interesting test for operator overloading which +shows that normal types without constant values may not suffice. +It is perhaps too much to try to support, but it is also a more reasonable use case for operator +overloading than say using `+` for slice concatenation. + +Binding methods to operators by means of pragma can simplify the type system. +The example above would provide a means of implementing some of the example based +contract bodies for variadic instantiation/boxing. +At any rate, all interfaces today only have listed methods, and having contracts +require operator overloading may be just too much in one shot. The example above +mostly shows that binding methods to operators can fit into this contracts +draft proposal in a way that to a reasonable extent addresses the wart of +using example based unary contracts as types. #### Discarded ideas From 6bd18a92d888dc08868f7ccb27c7632bce0ff09d Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 26 Sep 2018 23:00:19 +0200 Subject: [PATCH 19/23] add diff link inline --- design/go2draft-contracts.md | 1 + 1 file changed, 1 insertion(+) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 00c7331c..ff63a97a 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -7,6 +7,7 @@ August 27, 2018 Modified by Scott Cotton Sep 24, 2018 +[diff](https://github.com/golang/proposal/compare/master...wsc1:master) ## Abstract From e290908889c3443dd161f8ca3802259dddb2fc51 Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 27 Sep 2018 00:16:15 +0200 Subject: [PATCH 20/23] typo --- design/go2draft-contracts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index ff63a97a..6ad92ff3 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -2819,6 +2819,6 @@ this could similarly be rewritten to type anything(x T) interface {} ``` -After doing the above, we have unified types and interfaces and made it +After doing the above, we have unified contracts and interfaces and made it backward compatible (I think). From 91fc993e1598b6aef10b1e2d28b93e6a4997941c Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 27 Sep 2018 00:22:29 +0200 Subject: [PATCH 21/23] another typo fix --- design/go2draft-contracts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 6ad92ff3..6cf82e65 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -1928,7 +1928,7 @@ func MulsStatic(type T Multipliable)(vals ...T) T { } // the variadic/dynamic/boxed case -func MulsBoxed(a, b, c Multipliable) Multipliable { +func MulsBoxed(vals ...Multipliable) Multipliable { var res Multipliable = 1 for _, v := range vals { res = res * v From 50977e708ed01b2071482dcf05ba7c6b2374154e Mon Sep 17 00:00:00 2001 From: Scott Date: Thu, 27 Sep 2018 14:19:06 +0200 Subject: [PATCH 22/23] +discussion +minor fixes added a discussion section at the end minor fixes: replace value variable method list syntax type X(x T) interface { x: { } } with type X(x T) interface { T: { } } as it may be that type X(x, y T) interface { // .... } becomes useful. --- design/go2draft-contracts.md | 50 +++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index 6cf82e65..c934f72d 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -219,7 +219,7 @@ testing for equality, assignability, addressability, dereferencing pointers, etc Contract bodies have one special syntax in addition to the description of the types by means of examples. -In particular, a contract body may contain an example parameter, such as `x` in +In particular, a contract body may contain an type parameter, such as `T` in above, following by a colon `:`, followed by a bracketed list of method signatures as in Go's interfaces today. @@ -227,7 +227,7 @@ For example, one may write ```Go type stringer(x T) contract { - x: { + T: { String() string } } @@ -272,7 +272,7 @@ to implement the contract. For example ```Go type stringer(x T) contract { - x: { + T: { String() string } } @@ -522,7 +522,7 @@ as types in addition to as type parameters. An example follows. ```Go type stringer(x T) contract { - x: { + T: { String() string } } @@ -2804,7 +2804,7 @@ type X interface { syntactic sugar for ```Go type X(x T) interface { - x: { + X: { // ... as per Go 1 } } @@ -2812,13 +2812,49 @@ type X(x T) interface { Then we have anonymous interfaces to deal with as well ```Go -type anything interface{} +var x interface{} ``` this could similarly be rewritten to ```Go -type anything(x T) interface {} +var x (type _(x T) interface {}) +// (This require recognizing _ as an unnamed type.) ``` After doing the above, we have unified contracts and interfaces and made it backward compatible (I think). +## Discussion +This design has shown that contracts can be seen as an extension of interfaces. +I would imagine that it would be very convenient to avoid having separate +contracts for many existing interfaces. + +Some drawbacks have become clear in drafting this modification to the original +proposal. Mostly, there are example usages which forbid or currently forbid +being boxed. + +Another example is +```Go +type T(x X) contract { + *x +} +``` + +Such a contract cannot be boxed because Go doesn't allow derefencing boxed values, +unless it one day allows defining an operator for dereferencing. + +This phenomenon enlarges the space of "impossible contracts" substantially, which +is unfortunate. + +The counterbalance to these drawbacks are +* Many interfaces may need doubles as contracts if they are separate. +* Two constructs (contracts/interfaces) for overlapping functionality. +* non-orthogonality of constructs (defining type constraints vs applying them statically or dynamically) +* The drawbacks may reduce over time as user defined operators are considerred, whereas it +seems the complexity of implementing user defined operators may increase if contracts/interfaces are separate. + + + + + + + From 4e48dfa09faf9718661c1f6134d6bee1188926bd Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 3 Oct 2018 12:06:33 +0200 Subject: [PATCH 23/23] typo fix at end --- design/go2draft-contracts.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/design/go2draft-contracts.md b/design/go2draft-contracts.md index c934f72d..2922cdad 100644 --- a/design/go2draft-contracts.md +++ b/design/go2draft-contracts.md @@ -2804,7 +2804,7 @@ type X interface { syntactic sugar for ```Go type X(x T) interface { - X: { + T: { // ... as per Go 1 } } @@ -2842,8 +2842,8 @@ type T(x X) contract { Such a contract cannot be boxed because Go doesn't allow derefencing boxed values, unless it one day allows defining an operator for dereferencing. -This phenomenon enlarges the space of "impossible contracts" substantially, which -is unfortunate. +This phenomenon enlarges the space of "impossible contracts" substantially when +allowing unary contracts to act on runtime types, which is unfortunate. The counterbalance to these drawbacks are * Many interfaces may need doubles as contracts if they are separate. @@ -2852,6 +2852,30 @@ The counterbalance to these drawbacks are * The drawbacks may reduce over time as user defined operators are considerred, whereas it seems the complexity of implementing user defined operators may increase if contracts/interfaces are separate. +type a(x T) { + // contract goes here + +} /* only if type monodic */ interface { + // +} + + + +type a(x T) { +} struct { +} + +type Er interface { +} + + +type Er(x T) { +} interface { +} + + + +