Name resolution #
Names are how a program connects pieces of code. In Gleam, a name might refer to a function, a parameter, a local variable, a type, a constructor, a record field, or an imported module:
import gleam/io
fn greet(name) {
let message = "Hello, " <> name
io.println(message)
}
This program has several names: io, greet, name, message, and println.
The source text only tells us how those names are spelled. Name resolution works
out what each name refers to.
A common compiler definition is that name resolution connects references to the
declarations they denote.[1] That sounds small, but it is one of the places
where a language's rules become concrete. The compiler has to decide which names
are visible, what happens when two names are the same, and whether a name like
io.println means a module function or a field access.
For this project, the path is:
Gleam AST
-> scopes and symbols
-> resolved references
The parser keeps names as text. The resolver turns those names into stable symbol IDs and qualified member references that later compiler passes can use.
This chapter covers name resolution in more detail, including:
- lexical scope and shadowing
- bindings, references, scopes, and symbol tables
- namespaces, imports, and qualified names
- pattern bindings, visibility, and package graphs
- the resolver implemented in this compiler
Pierre Neron, Andrew Tolmach, Eelco Visser, and Guido Wachsmuth, "A Theory of Name Resolution": https://researchr.org/publication/NeronTVW15 ↩︎