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: Function alias #58897

Closed
xbt573 opened this issue Mar 6, 2023 · 16 comments
Closed

proposal: Go 2: Function alias #58897

xbt573 opened this issue Mar 6, 2023 · 16 comments
Labels
FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Milestone

Comments

@xbt573
Copy link

xbt573 commented Mar 6, 2023

Author background

  • Would you consider yourself a novice, intermediate, or experienced Go programmer?
    Intermediate
  • What other languages do you have experience with?
    Python, C#, JS/TS, Bash

Related proposals

  • Has this idea, or one like it, been proposed before?
    Probably not
  • Does this affect error handling?
    No
  • Is this about generics?
    No

Proposal

  • What is the proposed change?
    This proposal adds something like type aliases, but for functions
  • Who does this proposal help, and why?
    If we take code like this:
type SomeFunctionType func(string) string

func CreateSomeFunction() SomeFunctionType {
    return func(s string) string {
        return s
    }
}

var SomeFunction = CreateSomeFunction()

And then check documentation for SomeFunction, it will show:

var SomeFunction package.SomeFunctionType

It is very disappointing at first time

  • Please describe as precisely as possible the change to the language.
    This proposal adds new syntax:
func X=Y

If Y doc is:

func Y(str string) string

Then, X doc will be:

func X(str string) string
  • What would change in the language spec?
    New syntax, new chapter about function alias
  • Please also describe the change informally, as in a class teaching Go.
    Function alias helps create function type variables documentation easier
  • Is this change backward compatible?
    Yes
  • Orthogonality: how does this change interact or overlap with existing features?
    It doesn't
  • Is the goal of this change a performance improvement?
    No

Costs

  • Would this change make Go easier or harder to learn, and why?
    It can make Go a little bit harder to learn, but it will make documentation better in some cases
  • What is the cost of this proposal? (Every language change has a cost).
    New syntax realization
  • How many tools (such as vet, gopls, gofmt, goimports, etc.) would be affected?
    gopls, gofmt (maybe)
  • What is the compile time cost?
    Translating functions
  • What is the run time cost?
    None
  • Can you describe a possible implementation?
    No
  • Do you have a prototype? (This is not required.)
    No
@gopherbot gopherbot added this to the Proposal milestone Mar 6, 2023
@DeedleFake
Copy link

DeedleFake commented Mar 6, 2023

I don't understand what you're proposing. Your first example shows a closure being assigned to a variable, but your proposed syntax changes seem to be for creating a top-level function that is an alias for another top-level function. If that's correct, than this can already be accomplished by just doing func X(str string) string { return Y(str) }.

Also, your initial example has an error. SomeFunctionType is defined as func(string) string but CreateSomeFunction()'s implementation returns a func(string).

@xbt573
Copy link
Author

xbt573 commented Mar 6, 2023

Thanks for fix!
Yes, it can be made with func X(str string) string { return Y(str) }, but how this will work with closures?

func X(str string) string {
    return func(s string) string {
        return s
    }()
}

Like this?

@ianlancetaylor
Copy link
Contributor

How does this proposal help with closures?

@ianlancetaylor ianlancetaylor added LanguageChange Suggested changes to the Go language v2 An incompatible library change labels Mar 6, 2023
@earthboundkid
Copy link
Contributor

I do wish this worked:

package whatever

type fn func()

const f fn = func() {
  // ...
}

You have to use var instead.

@benhoyt
Copy link
Contributor

benhoyt commented Mar 6, 2023

@carlmjohnson How is a "const func" different to just using a named func (which are effectively constant and can't be reassigned)?

func f() {
  // ...
}

@DeedleFake
Copy link

DeedleFake commented Mar 6, 2023

How is a "const func" different to just using a named func (which are effectively constant and can't be reassigned)?

I thought the same thing and almost posted the same question, but there is actually one minor difference: Typing. A function declaration is always untyped, but a const could be typed, i.e. const F TypedFunc = f.

Instead of func consts, I wonder how feasible it would be to add a small syntax for applying a type to a regular function declaration.

@earthboundkid
Copy link
Contributor

You can add methods to typed functions, but you can't add methods to "constant" functions:

Not legal now:

package whatever

type fn func()

func (fn) foo() {}

const f fn = func() {
  // ...
}

func main() {
  f.foo()
}

Legal now:

package whatever

type fn func()

func (fn) foo() {}

func f() { /* */ }

func main() {
  fn(f).foo()
}

@xbt573
Copy link
Author

xbt573 commented Mar 7, 2023

How does this proposal help with closures?

This proposal helps make variable functions documentation better, closure is one example of many uses

@ianlancetaylor
Copy link
Contributor

@xbt573 I'm sorry, I don't understand. Can you show us a small example? Thanks.

@xbt573
Copy link
Author

xbt573 commented Mar 7, 2023

First example: Reduce boilerplate

We have code like this:

package pkg

func A(str string) string {
    return str + "A"
}

func B(str string) string {
    return str + "B"
}

func C(str string) string {
    return str + "C"
}

Using function alias, we can make this code tidier:

package pkg

type fn func(str string) string

func createFn(suffix string) fn {
    return func(str string) string {
        return str + suffix
    }
}

// func A(str string) string
func A = createFn("A")

// func B(str string) string
func B = createFn("B")

// func C(str string) string
func C = createFn("C")

Second example: Function ascension
For example, we have this folder structure:

test/
├── pkg.go
└── subpkg
    └── subpkg.go

subpkg.go defines structure and function to initialize it:

package subpkg

type Dummy struct{}

func NewDummy() *Dummy {
    return &Dummy{}
}

But we want user to use this function at package test, here we got function alias:

package test

import "test/subpkg"

// func NewDummy() *subpkg.NewDummy
func NewDummy = subpkg.NewDummy

If we use var for this, we will get strange documentation:

var A pkg.fn
var NewDummy func() *subpkg.NewDummy

@benhoyt
Copy link
Contributor

benhoyt commented Mar 7, 2023

First example: I don't actually think the first example is "tidier" -- to me it seems (slightly) more abstract and harder to understand. For this example, I'd use the fact that go fmt allows you to format one-statement functions into a single line (in reality here you'd just write str+"A" inline in each function, but I'm assuming that addSuffix did something complicated you wanted to factor out):

func A(str string) string { return addSuffix(str, "A") }
func B(str string) string { return addSuffix(str, "B") }
func C(str string) string { return addSuffix(str, "C") }

func addSuffix(str, suffix string) string {
	return str + suffix
}

Second example: I agree this would be nice, and I've wanted it a few times. However, I'm not sure the additional syntax is worth it. It's very easy just to write:

// NewDummy blah blah blah doc comment.
func NewDummy(args) *subpkg.Dummy {
    return subpkg.NewDummy(args)
}

It might be useful to see how often this kind of "trivial func wrapper" is done in a large corpus of Go code.

@apparentlymart
Copy link

type fn func(str string) string

func createFn(suffix string) fn {
    return func(str string) string {
        return str + suffix
    }
}

// func A(str string) string
func A = createFn("A")

This example seems to imply the Go compiler executing createFn at compile time in order to include the closure as part of the compiled program. Is that what you intended, or do I misunderstand how you intended this example to be treated?

I don't think there's any other situation where the Go compiler currently executes a function at compile time quite like this, but perhaps I'm missing something.

(Another possible interpretation that would avoid the compile time function evaluation would be to treat this as a shorthand for func A(s string) { return createFn("A")(s) }, but it would already be possible to write that so I assume that isn't the interpretation you intended.)

@xbt573
Copy link
Author

xbt573 commented Mar 8, 2023

This example seems to imply the Go compiler executing createFn at compile time

If I'm not mistaken, this function is executed at package import, here's example: https://go.dev/play/p/6bYKwjdBYiD
If we use proposed syntax (s/var/func/), this should work same

@benhoyt
Copy link
Contributor

benhoyt commented Mar 8, 2023

@xbt573 No, that's happening at runtime, at package init time (spec reference).

@ianlancetaylor
Copy link
Contributor

The benefits of this new syntax do not outweigh the significant costs of a language change. Also the emoji voting is not in favor. Therefore, this is a likely decline. Leaving open for three weeks for final comments.

@ianlancetaylor
Copy link
Contributor

No further comments.

@ianlancetaylor ianlancetaylor closed this as not planned Won't fix, can't repro, duplicate, stale May 10, 2023
@golang golang locked and limited conversation to collaborators May 9, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Projects
None yet
Development

No branches or pull requests

7 participants