-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Static name binding makes migrations difficult #7428
Comments
This causes a few issues, two of which we ran into over the course of the last ~three days:
This is exacerbated by the fact that official guidance is for native providers to be referenced using top-level symbols (e.g. Theoretical reasons to not do eager evaluation of symbols:
executes perfectly well.
This issue also surfaces with symbols in .bzl files: |
It's not eager evaluation, it's static name resolution and it's by design (https://github.com/bazelbuild/starlark/blob/master/design.md#static-name-resolution).
Indeed. Starlark is not as dynamic as Python. This has multiple advantages, in terms of tooling (IDE support, refactoring...), performance, and detecting errors early.
No language detects all errors statically, you have to draw a boundary. Starlark uses dynamic typing, so we cannot possibly know the methods of an object in the general case. |
My bad. I keep not remembering the term for, apparently "static name resolution". This has probably been discussed to death so I'll do my best not to re-litigate it, but is there a thread about the benefits of static name resolution to tooling? Naively, I'd think that pretending that Starlark has static name resolution if you are a tool would get you 99% of the way. |
I think the main argument is to detect errors early. We found lots of errors in the code when migrating out of Python. It's very difficult to get any kind of confidence when maintaining/updating/refactoring code, if you have no static checks. |
Naively, I would expect that all code paths are exercised, preferably in tests, but at least in code you run before committing your change... at least that's the usual wisdom dispensed about more dynamic languages when people ask about validity checks done by the compiler in more rigid languages. So is this a tradeoff between catching typos/refactoring issues early and ease of upgrading? |
which is very important for maintenance, especially as a code base gets bigger.
What you were asking for is to have the same file for multiple incompatible versions of Bazel. That's quite different. I certainly don't recommend to have code paths that depend on the Bazel version. So overall, you pretty much ask to change the language, just to handle your special-case - which is probably not going to happen. |
I've just had a look at the Wikipedia article. It mentions a recommendation from the Python manual, which says:
|
FTR, I don't think what Lukacs asks for is a special case. We see version checks in rules rust, rules_go, rules apple changed their design so they don't use top level symbols at all. All of this because of static name resolution. For these, I'll go as far as to say that the value of static name resolution is less than the cost of workarounds. I think Starlark could/should improve here, so its users don't have to go to great lengths fighting the language. If you think bigger codebases and better tooling are the focus of the Starlark language, consider #7468. I'd still want to see some way of type-safe reflection like the good old mirrors or smth similar. Or maybe all we need is for |
I'm reopening this issue because the actual problem (inability to determine whether a particular top-level symbol exists for compatibility between different Bazel releases) persists. I can imagine a number of other things that would help:
We are currently at a bad place: official policy is to prefer top-level symbols, they are hard to migrate to (or from, if need be) and we don't have any way to deter people from using old versions of Bazel. (the latter issue will get worse once we'll start having LTS versions, but let's not go that far...) I'm not asking for a language feature to solve my special case. I'm asking to reconcile the tension between (1) what Starlark does (static symbol resolution), (2) official policy (the encouragement of top-level symbols) and (3) reality (people don't upgrade Bazel all that often). It's just that the easiest solution I can think of is changing (1). If I would be fine with the How does Python want to solve the "I want to figure out if a top-level symbol exists" problem in a world where they are resolved statically? |
The choice of static name resolution is now baked pretty deeply into the interpreter design. Going dynamic would require a spec change in the language, and presumably a performance impact since we'd have to resort to hashing for name lookups instead of indexing a symbol table. The workaround of adding a In the case where a top-level symbol is deleted by an incompatible change, why not just migrate everyone before flipping the flag? Is the point to use the same .bzl to support even versions of Bazel that predate the introduction of the new API? That may cover this class of problems, but it's very difficult to generalize a way to write .bzl code that works across a wide range of interpreter / Bazel versions as both continue to evolve. (The traditional solution to that problem is to simply stop making breaking changes.) |
re: "why not just migrate everyone": that works in theory, but it makes it impossible to provide a version of Starlark code that works with a wide range of Bazel versions. The straightforward way would be something like "if symbol is present, do X, else do Y", but the static name binding makes that impossible. So we are stuck with fragmenting the ecosystem. It's certainly a problem, but how big a problem, I can't tell. We haven't ran into it recently. I think Bazel is different from Starlark-without-Bazel because it has a much wider interface and therefore changes to it are more likely. This problem is also exacerbated by the fact that we are pretty liberal with adding new top-level symbols (e.g. |
One approach we took in the past was to create separate bzl files, each with function implementations specific to a bazel version, and remote repository into which we symlinked one of these files depending on the actual Bazel version. Buildkite is pretty good at testing Starlark rules against multiple Bazel versions, but still maintaining these files gets hard very quickly as there are more incompatible flags. Second approach that can be taken by the API owner (rules_swift do this, and rules_rust are migrating towards this too) is to provide a single struct with functions that encapsulate all the possible global symbols (https://github.com/bazelbuild/rules_swift/blob/master/swift/internal/swift_common.bzl#L57 or https://github.com/bazelbuild/rules_rust/blob/main/rust/private/common.bzl#L29). This way users can use It may make sense to take a step back and look how users are using the language and see if all assumptions that led us to designing Starlark still hold. Does it still make sense to have a dynamically typed language when all the potential productivity wins are trumped by juggling with repositories and delegating functions? |
/cc @comius |
Thank you for contributing to the Bazel repository! This issue has been marked as stale since it has not had any activity in the last 2+ years. It will be closed in the next 14 days unless any other activity occurs or one of the following labels is added: "not stale", "awaiting-bazeler". Please reach out to the triage team ( |
This issue has been automatically closed due to inactivity. If you're still interested in pursuing this, please reach out to the triage team ( |
@Wyverald I think this is resolved by bazel_features. |
I don't think this should be treated as "fully resolved" by bazel_features. It certainly unblocks a lot of usage, but specifically the global name detection mechanism in bazel_features feels like a workaround. I'd vote to keep this open, but leaving it up to @brandjon to decide. (See also bazelbuild/starlark#218 (comment)) |
Please make Starlark resolve global accessible variable on runtime instead of on script loading phase.
This code fails if proto_common is not accessible as global variable. :
But in fact this line is ignored always during execution.
The text was updated successfully, but these errors were encountered: