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: Allow untyped parameters in function literals inside named types #68486

Closed
2 of 4 tasks
paskozdilar opened this issue Jul 17, 2024 · 2 comments
Closed
2 of 4 tasks
Labels
LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change
Milestone

Comments

@paskozdilar
Copy link

paskozdilar commented Jul 17, 2024

Go Programming Experience

Experienced

Other Languages Experience

C, C++, TypeScript, Python, Groovy

Related Idea

  • Has this idea, or one like it, been proposed before?
  • Does this affect error handling?
  • Is this about generics?
  • Is this change backward compatible? Breaking the Go 1 compatibility guarantee is a large cost and requires a large benefit

Has this idea, or one like it, been proposed before?

This idea has been originally proposed in #30931, but closed due to inadequate syntax.

ianlancetaylor commented on Apr 30, 2019:

Without a more persuasive notation we aren't going to adopt this proposal. Closing, but please free to comment, or open a new issue, if you have a different approach.

Does this affect error handling?

No.

Is this about generics?

No.

Proposal

In non-func named types, it is permitted to use untyped literals when instanciating:

type MyString string
type MyByteArray []byte
type MyInt int

_ = MyString("foo")
_ = MyByteArray(`bar`)
_ = MyInt(845)

This proposal extends this to a func named type:

type MyFunc func(string, int) (bool, error)

_ = MyFunc(func(str, n) (ok, err) { /* ... */ })

Since function arguments and return values are already defined in the named type, it should be easy to infer them in the untyped function literal.

This change would make named functions with long definitions less repetitive:

// before
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { /* ... */ }))

// after
http.Handle("/", http.HandlerFunc(func(w, r) { /* ... */ }))

Additionally, we could also allow parameter type inference in case variable-that-holds-a-func type is already defined:

var fn func(string, int) (bool, error);
fn = func(str, n) (ok, err) { /* ... */ }

Language Spec Changes

I am not that too familiar with technicalities of the spec, but I assume rules about anonymous functions would have to be relaxed to allow for untyped arguments:

FunctionLit = "func" Signature FunctionBody .

In particular, in ParameterDecl, the Type would be optional:

ParameterDecl = [ IdentifierList ] [ "..." ] [ Type ] .

Or the syntax itself should be separated, and FunctionLit should be defined with OptionallyTypedSignature which would have optional parameter declaration type.


This would require context-check of whether the function literal is inside of a named type or not:

type MyFunc func(string, int) (bool, error)

// OK
var MyFunc foo = func(str, n) (ok, err) { /* ... */ }

// OK
func SecondOrder(fn MyFunc)
SecondOrder(func(str, n) (ok, err) { /* ... */ })

// OK
fn := MyFunc(func(str, n) (ok, err) { /* ... */ })

// ERROR
fn := func(str, n) (ok, err) { /* ... */ }

I am not sure if this can be handled by syntax parsing itself or is this a semantic property.

Informal Change

In Go 1.22 and earlier, when creating anonymous functions, we had to specify all argument types in advance, regardless of whether or not .
That leads to a lot of unnecessary typing, e.g.:
 

type HandlerFunc func(ResponseWriter, Request)

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r Request) {
    f.HandlerFunc(w, r)
}

func NewCustomHandler() http.Handler {
    return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { /* ... */ })
}

This change would allow us to leave out the function parameter types:

func NewCustomHandler() http.Handler {
    return http.HandlerFunc(func (w, r) { /* ... */ })
}

Is this change backward compatible?

Yes.

// before
http.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    // ...
}))

// after
// or
http.Handle("/", http.HandlerFunc(func(w, r) {
    // ...
}))

Orthogonality: How does this change interact or overlap with existing features?

The new syntax is just a shorthand for the fully-typed function literal, similar to how name ":=" type "(" value ")" | value operator is a shorthand for "var" name type "=" value.

Would this change make Go easier or harder to learn, and why?

It wouldn't change the ease of learning Go significantly, similar to := operator.

Cost Description

Increased complexity of the spec and parser.

Changes to Go ToolChain

All tools that deal with language syntax.

Performance Costs

Compile time cost will probably be insignificant. Run time cost will be zero since code will compile exactly the same.

Prototype

I don't have the necessary experience with Go compiler to do this.

@paskozdilar paskozdilar added LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change labels Jul 17, 2024
@gopherbot gopherbot added this to the Proposal milestone Jul 17, 2024
@paskozdilar
Copy link
Author

Apparently, this was already discussed in #21498:

Unfortunately, func(a) {} already has a defined meaning: A function taking an unnamed parameter of type a.

Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change
Projects
None yet
Development

No branches or pull requests

3 participants