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: allow packages to be aliased #56611

Closed
thaJeztah opened this issue Nov 6, 2022 · 6 comments
Closed

proposal: allow packages to be aliased #56611

thaJeztah opened this issue Nov 6, 2022 · 6 comments
Labels
FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal Proposal-FinalCommentPeriod v2 An incompatible library change
Milestone

Comments

@thaJeztah
Copy link
Contributor

Proposal: allow packages to be aliased

This is a bit of a wild idea, and I have no idea if it's feasible (or even possible), so bear with me :)

When moving a package to a new location (or module), it is good practice to provide aliases for types and functions in the package, to provide users a transition period moving to the new package. Doing so tends to require a lot of boilerplating code to be created; "stubs", "aliases" or "wrappers";

package oldpackage

import "newpackage"

type (
	SomeType = newpackage.SomeType
	SomeOtherType = newpackage.SomeOtherType
)

const (
	SomeConst = newpackage.SomeConst
	SomeOtherConst = newpackage.SomeOtherConst
)

func SomeFunc() error {
	return newpackage.SomeFunc()
}

// or

var SomeOtherFunc = newpackage.SomeOtherFunc

What I tried

Out of curiosity, I wondered if a "dot imports" would work for this. As an example, I took the golang.org/x/crypto/ed25519 package, which is now a wrapper for stdlib's crypto/ed25519;

package ed25519

import . "crypto/ed25519"

var _ = PublicKeySize // needed to prevent the import from being considered "unused"

The first issue already appeared (not using anything of the import locally would make it "unused"); a quick hack was to add a _ = assignment. But of course, this approach won't work; I realized that while "dot imports" provide the imported functions and types "locally", they're not exported (by design), so users of the package (including the ed25519_test package) won't see it;

# golang.org/x/crypto/ed25519_test [golang.org/x/crypto/ed25519.test]
./ed25519_test.go:18:17: undefined: ed25519.Sign
./ed25519_test.go:19:14: undefined: ed25519.Verify
FAIL	golang.org/x/crypto/ed25519 [build failed]
FAIL

Proposal: allow a package as a whole to be aliased

What if instead, it would be possible to alias the package as a whole, instead of having to create aliases or wrappers for every type and function in a package, which could look something like;

// Package oldpackage provides tools to interact with flux-capacitors.
//
// Deprecated: use [crypto/ed25519] instead.
package oldpackage = "github.com/foo/bar/newpackage"

Being able to alias a package brings some advantages;

  • It reduces boilerplating code when moving a package, as it allows aliasing a package for backward compatibility instead of having to create aliases or wrappers for every type and function in a package.
  • As no "stubs" / "wrappers" are needed for individual functions, it can assist in preserving git history; files can be moved as-is (baring the "package" name), where previously there could be a risk of breaking history if the new "stubs" file would have the same name as the previous file.

A good proposal wouldn't include disadvantages, so here's some;

  • It may increase the risk of (implicitly) introducing breaking changes, for example, when both moving a package and changing signatures.
  • Limited use if only some types / functions are moved (package split into multiple packages).
@gopherbot gopherbot added this to the Proposal milestone Nov 6, 2022
@ianlancetaylor ianlancetaylor added LanguageChange Suggested changes to the Go language v2 An incompatible library change labels Nov 6, 2022
@apparentlymart
Copy link

Hi @thaJeztah!

This does seem like an interesting idea, and I did at one point do a reorganization of packages that might have benefited from something like this, although in my case my packages were each small enough and coupled enough that I was able to move quickly through hand-writing some minimal stubs, updating the callers to the new packages, and then deleting the stubs.

Rather than extending the language or main toolchain, I wonder about the feasibility of addressing this using external tooling. Would it be possible to write a program that can discover the entire exported API of a given package and produce equivalent wrapper stubs for all of it automatically? My understanding is that the introduction of type aliases completed the set of features required to successfully "redirect" all exported API of existing packages and so I would expect this to be possible, though I imagine it could get quite complicated in practice.

I acknowledge that this alternative doesn't fully address your concern about preserving version control history, but I imagine it could do so if the convention were to name the generated stubs file something that is unlikely to conflict with any existing source file, such as refactoring_stubs.go, and thus e.g. the rename-detection heuristic in Git should still be able to track the movements of the real files to their new locations as long as they remain substantially similar.

@ianlancetaylor
Copy link
Contributor

This is only useful if the package is being moved with no changes to the API. In many cases when a package moves there is also a desire to clean up the API.

We agree with @apparentlymart that a tool to write aliases seems like the right approach. The output of the tool can serve as the basis for the new package (or vice-versa if appropriate).

Therefore this is a likely decline. Leaving open for four weeks for final comments.

-- for @golang/proposal-review

@bcmills
Copy link
Contributor

bcmills commented Dec 8, 2022

We agree with @apparentlymart that a tool to write aliases seems like the right approach. The output of the tool can serve as the basis for the new package (or vice-versa if appropriate).

Note that I prototyped such a tool in CL 137076, if anyone wants to mine that for ideas or polish it up to production grade.

@bcmills
Copy link
Contributor

bcmills commented Dec 8, 2022

This is only useful if the package is being moved with no changes to the API.

Note that #26904 considers that at the module level, and we are also thinking about how to achieve it at the package level in cmd/go — but that likely won't require a change to the language proper. (Per the spec, “The interpretation of the ImportPath is implementation-dependent….”)

@thaJeztah
Copy link
Contributor Author

Therefore this is a likely decline. Leaving open for four weeks for final comments.

Thanks all for your input / suggestions! I'd fully understand if this suggestion would not be accepted (it was a bit of a "wild" idea).

My motivation for opening this ticket was that I ran into this situation quite a few times, so thought "likely others would have run into this as well", and maybe there already is an elegant solution? My experience on various projects I help maintain is that many feature requests on their own may not be "good" or "acceptable", but reading through them can reveal "clusters" (for better words) of tangentially related issues, which can help with future enhancements ("Oh, I seen ideas around X, Y, an Z: it looks like we can improve things in that area; what if instead, we'd do "Foo"?).

Would it be possible to write a program that can discover the entire exported API of a given package and produce equivalent wrapper stubs for all of it automatically?

Hm.. yes, I guess something like that would be possible. To be fair; in most cases it's not a huge effort to create the stubs; a bit of elbow grease and 15-20 minutes of work would do the trick for most packages.

Note that I prototyped such a tool in CL 137076, if anyone wants to mine that for ideas or polish it up to production grade.

Ah, nice; yes, perhaps in g/x/tools could be something; to be fair; it depends on how much maintenance it would require and if it would be within scope of g/x/tools. I could give it a spin for testing though.

Note that #26904 considers that at the module level, and we are also thinking about how to achieve it at the package level in cmd/go — but that likely won't require a change to the language proper. (Per the spec, “The interpretation of the ImportPath is implementation-dependent….”)

Interesting; let me try giving that a more thorough read to see how that would work, thanks!

@ianlancetaylor
Copy link
Contributor

No change in consensus.

@ianlancetaylor ianlancetaylor closed this as not planned Won't fix, can't repro, duplicate, stale Jan 4, 2023
@golang golang locked and limited conversation to collaborators Jan 4, 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

5 participants