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: Not import packages #42232

Closed
pjebs opened this issue Oct 27, 2020 · 12 comments
Closed

proposal: Go 2: Not import packages #42232

pjebs opened this issue Oct 27, 2020 · 12 comments

Comments

@pjebs
Copy link
Contributor

@pjebs pjebs commented Oct 27, 2020

I am proposing the concept of a "not import" which can reduce dependencies and file sizes, and perhaps assist with dead code elimination.

The concept is better elucidated by explaining my use case (and showing that using interfaces is inadequate):

  1. I have a package called dbq github.com/rocketlaunchr/dbq/v2.
  2. It provides extensions to database/sql but is designed to work with mysql driver and postresql driver only.
  3. It is a package designed for use by other projects as a dependency. It is not a standalone executable.
  4. A typical consumer of the package will only ever use it with mysql xor postgres driver. Never both.
  5. Under the status quo, the package has to import both drivers because it needs to type switch the errors returned by database/sql to compare it with mysql specific errors and postgres specific errors to perform specific behaviours:

Hypothetical example:

import (
	~ "github.com/lib/pq"
	~ "github.com/go-sql-driver/mysql"
)

_, err := db.QueryRow("query", id).Scan(&nt)
switch v := err.(type) {
case nil:
	return nil
case *mysql.MySQLError:
	return v.Number
case *pq.Error:
	return v.Code
default:
}
  1. In the type switch above, if the consumer project (or any other dependency of the consumer project) does not import mysql or pq, then the compiler will just ignore that case.

  2. For comma, ok form type assertions, ok will return false

  3. For standard type assertions, if the consumer project (or any other dependency of the consumer project) imports the mysql or pq package, it will work as per normal. Otherwise the compiler exits with an error.

  4. Any func init() inside a "not import" will not run unless the consumer project (or any other dependency of the consumer project) imports it.

  5. If there are any exported functions with arguments or return values that rely on the import, then compiler exits with an error => programmer must change it from a "not import"

  6. For non-exported functions, the compiler will eliminate the function (dead code elimination) based on code path.

NOTE:

I haven't had time to think it through comprehensively. I already anticipate some issues. At this stage, please interpret my proposal with regards to the spirit and objective of what I'm trying to achieve and provide feedback and insight below so that I can refine it.

@gopherbot gopherbot added this to the Proposal milestone Oct 27, 2020
@gopherbot gopherbot added the Proposal label Oct 27, 2020
@pierrec
Copy link

@pierrec pierrec commented Oct 27, 2020

why not provide subpackages: one for mysql and one for pgsql?

@mvdan
Copy link
Member

@mvdan mvdan commented Oct 27, 2020

My initial reaction is that dash and underscore imports would be very similar, and likely to be misread/confused:

import (
    - "foo"
    _ "bar"
    - "baz"
)

It's also not immediately obvious what - means. _ already has a broad meaning of "ignore the following" in Go, and with dot-imports, . follows how the dot is part of qualified names from imported packages.

@pjebs
Copy link
Contributor Author

@pjebs pjebs commented Oct 27, 2020

Dash means negative which means "don't include". The specific syntax is not important. It could be anything including a # or : or ~

import (
    - "foo"
    _ "bar"
    ~ "baz"
    # "baz"
    : "baz"
)
@dsnet
Copy link
Member

@dsnet dsnet commented Oct 28, 2020

For this specific case, couldn't you use reflection to access the relevant data without a hard dependency on those error types?

_, err := db.QueryRow("query", id).Scan(&nt)
if err == nil {
    return  0
}
switch verr := reflect.Indirect(reflect.ValueOf(err)); {
case verr.PkgPath() == "github.com/go-sql-driver/mysql" && verr.Name() == "MySQLError":
    return v.FieldByName("Number").Interface()
case verr.PkgPath() == "github.com/lib/pq" && verr.Name() == "Error":
    return v.FieldByName("Code").Interface()
}
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 28, 2020

This is related to #38450, which addresses dead code elimination in a different way. If all of a package's code is eliminated, then the remaining type assertions for code like this don't seem like much to worry about.

@pjebs
Copy link
Contributor Author

@pjebs pjebs commented Oct 28, 2020

@dsnet Don't necessarily interpret this proposal are related to my use case. Think of it in terms of a benefit to Go and the Go ecosystem. I've been thinking about this problem even before I encountered my specific use case.

Finally, your approach loses static type checking and not pretty.

@dsnet
Copy link
Member

@dsnet dsnet commented Oct 28, 2020

Don't necessarily interpret this proposal are related to my use case.

It's hard to realistically evaluate a proposal that is divorced from the concrete use cases that the feature would be useful for.

Finally, your approach loses static type checking

True, but Go reflection is a approach that works today without the addition of an entire language feature with many subtleties involved.

@pjebs
Copy link
Contributor Author

@pjebs pjebs commented Oct 28, 2020

reflection also allows the consumer to "trick" the package with a different package but with the same package path name.

@pjebs
Copy link
Contributor Author

@pjebs pjebs commented Oct 28, 2020

@ianlancetaylor I believe #38450 is about indicating that a certain package should not call its init function.

This proposal is about indicating to the compiler/linker that a certain dependency need not be bundled in the executable.

The emphasis is different.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 28, 2020

I agree that the emphasis is different, but the result may be similar. When addressing a problem, a tooling change is normally preferable to a language change.

@pjebs pjebs changed the title proposal: Go 2: Dash import packages proposal: Go 2: Not import packages Oct 28, 2020
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Nov 10, 2020

Based on the discussion above, this is a likely decline. Leaving open for four weeks for final comments.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Dec 15, 2020

No further comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants