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: a new read-only type proposal #29392

Closed
zigo101 opened this issue Dec 22, 2018 · 4 comments
Closed

proposal: Go 2: a new read-only type proposal #29392

zigo101 opened this issue Dec 22, 2018 · 4 comments
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@zigo101
Copy link

zigo101 commented Dec 22, 2018

There has already been many read-only types related proposals.
But I feel they are too complicated to destroy the simplicity of Go.
So here I try to make a simpler one.

I propose to add a type literal modifier, @, which means assignment sign = (The literal choice is not the core of this proposal, it can be designed in a different style.).
A read-only type with a foundation type T can be represented as @T, which means values of @T can only be used as source values and can't be used as destination values.

A value of a read-only type must be bound to (a copy of) of another value.
If it is a variable, the binding happens when it is declared.
This means a read-only value is addressable and can be taken address.
But (the direct part of) a read-only value can never be modified (after the value is declaration).

[update 4]:
A value of type @T can always be assigned to a value of type T, the reason is in the assignment process, a value of type @T is copied to the T value.
A value of type T can be assigned to a value of type @T, but only when the @T value is declared.
In other words, values of @T can be implicitly converted to type T, and vice versa. (conversion != assignment)
What prevents a value of a source type from being assigned to a value of a destination type is whether or not the source type has a more restricted base/element type than the destination type.
The read-only in this proposal means that, for example, if the base type of a pointer value is read-only, then compilers will forbid modifying the value referenced by the pointer through the pointer, but the value referenced by the pointer can be modified through other pointers referencing the value, or the value can even be modified directly. Surely, if the type of a value is read-only, then there are no ways to modify the value. It is just that mutable values sometimes can be viewed as read-only values, but read-only values will never be viewed as mutable values.

One example:

	var x @int = 123
	var p *@int
	p = &x
	*p = 789        // error: the value referenced by p is immutable
	fmt.Println(p)  // ok
	fmt.Println(*p) // ok
	var p2 *int
	p2 = p // error: type mismatch
	p = p2 // ok

Another example:

	var book = @struct {
		pages int
		title string
	} {
		pages: 256,
		title: "Go 101",
	}
	var p = &book         // the type of p is *@struct {pages int; title string}
	var pa = &book.pages  // the type of pa is *@int
	var pb = &book.string // the type pb is *@string
	var _ = *p       // ok
	var _ = *p.pages // ok
	book.pages = 300 // error: book is an immutable value
	*pa = 300        // error: the value referenced by pa is immutable

In fact, @struct {pages int; title string} and @struct {pages @int; title @string} are equivalent. The same, @[N]T and @[N]@T are equivalent.

  • struct {pages @int; title string} is not a valid type literal.
  • [N]@T is not a valid type literal.

Strings can be viewed as read-only byte containers.

	var s = "abc"
	var p = &s[0]   // ok. The type of p is *@byte.
	fmt.Println(p)  // ok
	var _ *byte = p // error: type mismatch

Slices:

	var langs = @[]@string {"C", "Go"}
	langs[1] = "C++" // error: elements of langs are immutable
	langs = nil      // error: langs is immutable
	
	var primes = @[]int {2, 3, 5}
	primes[0] = 7 // ok
	primes = nil  // error: primes is immutable
	
	var bls = []@bool {false, true}
	bls[0] = false // error: elements of bls are immtable
	bls = nil      // ok

	var ss = []string{"abc", "mno", "xyz"}
	langs = ss[:2] // ok
	ss = langs[:1] // error: type mismatch

The meaning of the @ modifier is a little different for map types.

  • A value of type map[@K]T means no new keys are allowed to be appended into the map value.
  • A value of type map[@K]@T means no new enries are allowed to be appended into the map value.
  • Type map[K]@T is equivalent to type map[@K]@T

For channel types:
chan @int and chan int are equivalent. chan <-@int and chan <-int are equivalent. <-chan @int and <-chan int are equivalent. This means when a value is received from a chan <-@int channel, the type of the value can be viewed as either @int or int. But for the deterministic in reflection functions and compatibility, its type should be viewed as int. ([update 2]: For reflection purpose, chan @int and chan int should not be viewed as equivalent types.)

The method set of T is a super set of @T. The method set of *@T is a super set of @T. The method set of *T is a super set of *@T.

In implementations, the proposal needs an extra bit stored in a pointer value to indicate the value referenced by the pointer is immutable. For 64-bit archs, this is not a big problem, but it may be a drawback for 32-bit archs. Besides this drawback, this proposal may cause a small overhead for pointer operations. (no these drawbacks after thinking for awhile, for the read-only info is stored with types instead of values.)

[update 1]
For most scenarios, whether or not the dynamic type of an interface value is read only is not important. But for reflection purpose, making the difference is needed. So the read-only info will be kept in the dynamic type info stored in an interface value.

	var x @int
	var y int
	var i interface{} = x
	_ = i.(int)  // panic (or not panic. Either design decision is ok, I think.)
	_ = i.(@int) // ok
	i = y
	_ = i.(int)  // ok
	_ = i.(@int) // panic (or not panic. Either design decision is ok, I think.)

[update 2]: see above channel section.

[update 3]
For functions, it is a nonsense to make function results read only. In other words, though syntax doesn't forbid read-only results, but in practice, read-only results are almost totally useless, for their final values will be always zero values. Function results should be always not read only. It is a nonsense to make function parameters and results read only. The reason is arguments and returns of function called are always copied. But if a parameter or a result is of a container (or pointer) type, the read-only info for its element type (or base type) is important.

	var f0 func([]@int) []@bool
	var f1 func([]int) []@bool
	var f2 func([]@int) []bool
	var f3 func([]int) []bool
	
	f0 = f1 // error: parameter types mismatch
	f1 = f0 // ok
	
	f0 = f2 // ok
	f2 = f0 // error: result types mismatch
	
	f0 = f3 // error: parameter types mismatch
	f3 = f0 // error: error: result types mismatch
	
	f1 = f2 // ok
	f2 = f1 // error: both parameter and result types mismatch
	
	f1 = f3 // ok
	f3 = f1 // error: result types mismatch
	
	f2 = f3 // error: parameter types mismatch
	f3 = f2 // ok

For clarity purpose, using read-only parameters and results should be forbidden.
(In fact, read-only parameters and results of a function are not totally non-sense, they are just non-sense for callers of the function. They are still meaningful in the function internal. In other words, in the API docs of a function, none of the parameters and results should present as read-only. Function type func (@T1) @T2 should be equivalent to function type func (T1) T2.)

BTW, in fact, a function declaration can be viewed as a read-only function variable declaration:

func foo(T1) T2 {
	...
}

is equuvalent to

var foo = @func(T1) T2 {
	...
}

[update 4]: see one of the beginning paragraphs.

@gopherbot gopherbot added this to the Proposal milestone Dec 22, 2018
@zigo101
Copy link
Author

zigo101 commented Dec 22, 2018

Ah, this proposal has many similar points with #29192, but there are also many differences for all kinds of details.

@rsc rsc added v2 A language change or incompatible library change LanguageChange labels Jan 9, 2019
@ianlancetaylor ianlancetaylor changed the title proposal: a new read-only type proposal proposal: Go 2: a new read-only type proposal Jan 9, 2019
@ianlancetaylor
Copy link
Contributor

This proposal seems susceptible to const-poisoning, in that @ has to spread throughout the program.

@ianlancetaylor
Copy link
Contributor

Is this any different than #22876 or #29192, except for changing ro to @?

@zigo101
Copy link
Author

zigo101 commented Jan 9, 2019

The @ is just syntax, it can be anything else. It has many same points as #22876 and #29192, but it also have many detailed differences from those two.

I do prefer to my another proposal now.

I can close this proposal.

@zigo101 zigo101 closed this as completed Jan 9, 2019
@golang golang locked and limited conversation to collaborators Jan 10, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

4 participants