Skip to content

Add manual chapter on world age and binding partition #58253

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

Merged
merged 4 commits into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base/Base_compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ support libraries, etc. In these cases it can be useful to prevent unwanted
method invalidation and recompilation latency, and to prevent the user from
breaking supporting infrastructure by mistake.

The current world age can be queried using [`Base.get_world_counter()`](@ref)
The global world age can be queried using [`Base.get_world_counter()`](@ref)
and stored for later use within the lifetime of the current Julia session, or
when serializing and reloading the system image.

Expand Down
69 changes: 2 additions & 67 deletions doc/src/manual/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,73 +578,8 @@ However, future calls to `tryeval` will continue to see the definition of `newfu

You may want to try this for yourself to see how it works.

The implementation of this behavior is a "world age counter".
This monotonically increasing value tracks each method definition operation.
This allows describing "the set of method definitions visible to a given runtime environment"
as a single number, or "world age".
It also allows comparing the methods available in two worlds just by comparing their ordinal value.
In the example above, we see that the "current world" (in which the method `newfun` exists),
is one greater than the task-local "runtime world" that was fixed when the execution of `tryeval` started.

Sometimes it is necessary to get around this (for example, if you are implementing the above REPL).
Fortunately, there is an easy solution: call the function using [`Base.invokelatest`](@ref) or
the macro version [`Base.@invokelatest`](@ref):

```jldoctest
julia> function tryeval2()
@eval newfun2() = 2
@invokelatest newfun2()
end
tryeval2 (generic function with 1 method)

julia> tryeval2()
2
```

Finally, let's take a look at some more complex examples where this rule comes into play.
Define a function `f(x)`, which initially has one method:

```jldoctest redefinemethod
julia> f(x) = "original definition"
f (generic function with 1 method)
```

Start some other operations that use `f(x)`:

```jldoctest redefinemethod
julia> g(x) = f(x)
g (generic function with 1 method)

julia> t = @async f(wait()); yield();
```

Now we add some new methods to `f(x)`:

```jldoctest redefinemethod
julia> f(x::Int) = "definition for Int"
f (generic function with 2 methods)

julia> f(x::Type{Int}) = "definition for Type{Int}"
f (generic function with 3 methods)
```

Compare how these results differ:

```jldoctest redefinemethod
julia> f(1)
"definition for Int"

julia> g(1)
"definition for Int"

julia> fetch(schedule(t, 1))
"original definition"

julia> t = @async f(wait()); yield();

julia> fetch(schedule(t, 1))
"definition for Int"
```
The implementation of this behavior is a "world age counter", which is further described in the [Worldage](@ref man-worldage)
manual chapter.

## Design Patterns with Parametric Methods

Expand Down
62 changes: 62 additions & 0 deletions doc/src/manual/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,68 @@ Here, Julia cannot decide which `f` you are referring to, so you have to make a

3. When the names in question *do* share a meaning, it is common for one module to import it from another, or have a lightweight “base” package with the sole function of defining an interface like this, which can be used by other packages. It is conventional to have such package names end in `...Base` (which has nothing to do with Julia's `Base` module).

### Precedence order of definitions

There are in general four kinds of binding definitions:
1. Those provided via implicit import through `using M`
2. Those provided via explicit import (e.g. `using M: x`, `import M: x`)
3. Those declared implicitly as global (via `global x` without type specification)
4. Those declared explicitly using definition syntax (`const`, `global x::T`, `struct`, etc.)

Syntactically, we divide these into three precedence levels (from weakest to strongest)
1. Implicit imports
2. Implicit declarations
3. Explicit declarations and imports

In general, we permit replacement of weaker bindings by stronger ones:

```julia-repl
julia> module M1; const x = 1; export x; end
Main.M1

julia> using .M1

julia> x # Implicit import from M1
1

julia> begin; f() = (global x; x = 1) end

julia> x # Implicit declaration
ERROR: UndefVarError: `x` not defined in `Main`
Suggestion: add an appropriate import or assignment. This global was declared but not assigned.

julia> const x = 2 # Explicit declaration
2
```

However, within the explicit precedence level, replacement is syntactically disallowed:
```julia-repl
julia> module M1; const x = 1; export x; end
Main.M1

julia> import .M1: x

julia> const x = 2
ERROR: cannot declare Main.x constant; it was already declared as an import
Stacktrace:
[1] top-level scope
@ REPL[3]:1
```

or ignored:

```julia-repl
julia> const y = 2
2

julia> import .M1: x as y
WARNING: import of M1.x into Main conflicts with an existing identifier; ignored.
```

The resolution of an implicit binding depends on the set of all `using`'d modules visible
in the current world age. See [the manual chapter on world age](@ref man-worldage) for more
details.

### Default top-level definitions and bare modules

Modules automatically contain `using Core`, `using Base`, and definitions of the [`eval`](@ref)
Expand Down
Loading