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: merge Type Assertion notation x.(T) into Explicit Conversion notation T(x) #33480

Closed
gtanguy opened this issue Aug 5, 2019 · 6 comments
Labels
Milestone

Comments

@gtanguy
Copy link

gtanguy commented Aug 5, 2019

Go defines two different notations for similar concerns : type checking and changing

Motivation

I think type assertions and type switches notations are surprising, especially for Go newcomers, not standard, and almost redundant with Conversions notation :

  • x.(T) notation isn't common in popular programming languages, evokes methods with return parameters or selector notation for interface and struct embedding. Citing Ian Lance Taylor : The "." operator is heavily overloaded in Go. […] There is no special reason for this [use in type assertion] except that it is unambiguous and looks good on the page.
  • Type assertions and Conversions are often presented together in Go tutorials and rise questions from Go newcomers. Yet both notations are almost disjoint : Type assertion is used for an expression of interface type and Explicit conversion for other types (except for asserting or converting an expression of interface type into another interface, see below)
  • two-forms notation (v, ok = x.(T) and v = x.(T)) is said to be similar to maps but is actually different : when retrieving an element from a map, both notations elem, ok = m[key] and elem = m[key] return zero value if key isn't found, while for type assertion, v = x.(T) triggers a run-time panic if the assertion is false.

Proposal

Merge these notations into one unique notation T(x) modeled on Conversion notation :

  • Conversion remains v = T(x) and is used when dynamic type assertion isn't needed : x isn't of interface type, or if type of x and T are both interfaces and T is a subset of x's interface type (to satisfy Assignability rule in conversions)
  • Type assertion v, ok = x.(T) becomes v, ok = T(x) and is used otherwise. Boolean return parameter is not optional anymore.
  • Type switches switch x.(type) {} becomes : switch type(x) {}
  • Keep current Type assertions/switch notation for Go1 backward compatibility, but mark them obsolete in go vet

Benefits

  • simplicity and memorability : more familiar and same notation for similar concepts. "Differences from more familiar programming languages" is one of the top three major challenges identified in Go 2018 Survey
  • safer : no more run-time panic on type assertion
  • with distinct return parameters, keep a visible difference between dynamic type assertion (at compile-time) and static type conversion (at run-time) to help Go developers to understand this difference

Drawbacks

  • form of type assertion with only one return parameter v = x.(T) won't be possible anymore. Reasons :
    • to resolve ambiguity when both operands are of Interface type and for backward compatibility
    • to let developers know whether (static) type conversion or (dynamic) type assertion is performed.

Exemple

type i1 interface {
	M() int
	N() int
}
type i2 interface {
	M() int
}
func f(v1 i1) {
	var v2, ok = i2(v1) // check dynamic type of v1 is i2
	var v3 = i2(v1)     // check i1 implements i2
	fmt.Println(v2, ok)
	fmt.Println(v3)
}
@gopherbot gopherbot added this to the Proposal milestone Aug 5, 2019
@ianlancetaylor ianlancetaylor added v2 A language change or incompatible library change LanguageChange labels Aug 5, 2019
@ianlancetaylor
Copy link
Contributor

The biggest advantage of the current notation: type conversions never fail, type assertions do.

If I understand this proposal correctly, you are shifting the notation so that the ", ok" form is required for type assertions. If we make that required, then we no longer need to distinguish the cases with a different syntax.

But it would be rather annoying to have to rewrite all existing simple type assertions to use the ", ok" form.

@gtanguy
Copy link
Author

gtanguy commented Aug 6, 2019

Thanks for reading this proposal.

The biggest advantage of the current notation: type conversions never fail, type assertions do.

With the current notation, we have two way to know type assertion has not succeeded : with the boolean return parameter and by run-time panic. This proposal just removes the run-time panic but keeps a distinction between type conversions which always succeed and type assertions which don't.

But it would be rather annoying to have to rewrite all existing simple type assertions to use the ", ok" form.

Since we keep the current syntax for backward compatibility, it may be done in a smooth transition, and give an opportunity for developers to check there's no unsafe type assertions in their code.

To be honest, programming languages I know have an unsafe cast operator which generates run-time error, even if their documentation warns against them : Java typecast generate ClassCastException, kotlin has unsafe "as" and safe "as?", swift with the conditional "as?" and forced "as!"

@deanveloper
Copy link

deanveloper commented Aug 6, 2019

The current way that Go does it works very well I think, and I think they both translate to natural language well.

uint(x) // "the uint version of x"
foo.(Bar).Baz // "turn foo into Bar, then get Baz"

@ianlancetaylor
Copy link
Contributor

If we are going to keep the current syntax, which I agree makes sense, then I don't see the benefit of this change. We don't need two different ways to do the same thing.

Your argument is, I think, that conversions and type assertions are similar, and should be expressed similarly. But they aren't all that similar, and even in this proposal they are expressed somewhat differently, albeit less differently than they are expressed now. I don't really see what we gain by making this change.

@ianlancetaylor
Copy link
Contributor

For the reasons given in the last comment, this is a likely decline.

Leaving open for a month for final comments.

@ianlancetaylor
Copy link
Contributor

There were no further comments.

@golang golang locked and limited conversation to collaborators Sep 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants