Name Resolution
Names are the currency of modules. Names flow into modules through private imports and extended imports sections. Names flow out of modules through introduced names and extended imports sections.
Names are not constrained to be globally unique within a project, as this would be a heinous requirement. Even following perfectly reasonable (but not Procrustean) naming conventions, it is extremely likely that unaffiliated vendors would introduce name collisions. Despite having the ability to import a name under a new name, a programmer would find name selection an onerous task.
Every name is associated with an atom that is created at the point of the name's original introduction. Think of an atom as a discrete particle of identity. Most values in Avail are identityless, so atoms are interesting just by dint of having identity. As such, it is occasionally useful to resolve a name to an atom for no other reason than to obtain the atom. It is more often the case, however, that a dependent module imports a name because it wishes to use the associated method in its body.
There are only two ways to introduce a name:
- By mentioning the name, as a string literal, in an introduced names section.
- By resolving a name to an atom when no atom in scope corresponds to that name. All names introduced in this fashion are private to the resolving module (unless dynamically exported with
"Export_as a new name"
).
There are two ways to reference a name within a module body:
- By sending a message that resolves a name to an atom, such as
"Method_is_"
or"atom for_"
, to a string that represents a name. - By using the name to send a message. In this case, the compiler parses the source text to identify the names that correspond to expressions. For example, the compiler parses
1 + 3
to discover a send of"_+_"
. Once the name has been determined, the compiler uses available type information to resolve it to an atom.
Module resolution is the process by which names are resolved to atoms.
- The name to resolve is provided as a string to name-resolving method.
- In this case, name resolution begins with a send of a name-resolving method to a string that represents the name to resolve. Let us call this name
N
. Name resolution proceeds by looking upN
in the resolving module. - The name to resolve is sent as a message.
- In this case, name resolution begins with a send of a message. The compiler determines which message is being sent by parsing the related expression. Let us call this message
N
. Name resolution proceeds by looking upN
in the resolving module. - Look up the atoms associated with the name.
- Look up any atoms associated with
N
in the resolving module. These include any atoms 1) created by including a name in the introduced names section, 2) imported from an upstream module, or 3) created by a previous lookup conducted by the resolving module. LetC
denote the set of candidate atoms:
whereC ≝ {A0, A1, …, An}
,A0
throughAn
are the candidate atoms. Decide how to proceed based on thecardinality
ofC
:|C| = 0
: Create a new atom that is private to the resolving module.|C| ≠ 0
: Determine whether any of the candidate atoms was created locally.
- Create a new private atom.
N
does not refer to an existing atom. Create a new atom,A
, and associate it withN
. This atom will remain private to the resolving module unless it is dynamically exported by"Export_as a new name"
. Name resolution has succeeded:N
is resolved toA
.- Is the name associated with a locally defined atom?
- Does
N
refer to an atomAi
that was created by the resolving module? If so, then name resolution has succeeded: resolveN
toAi
, even if there are other candidate atoms, i.e.,C ≠ {Ai}
. If not, then pay closer attention to the cardinality ofC
. - Just how many atoms are associated with the name?
- Because none of
A0
…An
are locally defined atoms, re-examine the cardinality ofC
:|C| = 1
: Only one imported atom,A0
, is namedN
, thereforeC
is
Name resolution has succeeded:C ≝ {A0}
.N
is resolved toA0
.|C| ≥ 2
:N
is a viable name for any element ofC
, soN
has surface ambiguity. It may be possible to use type information to further disambiguateN
.
- Is type information available to disambiguate the name?
- Is any argument type information available to assist in the disambiguation of
N
? Such information is only available whenN
is being sent as a message. If argument type information is available, then filter the choice of atom based on the visibility of method definitions. If not, then name resolution has failed:N
is semantically ambiguous. - Use type information to filter the candidate atoms.
- Method arguments are statically typed. Use this type information to determine which visible method definitions are applicable at the call site of
N
. LetC′
be the set of atoms that correspond to methods with applicable definitions. The expansion ofC′
is
whereC′ ≝ {Ai, Aj, Ak, …}
,Ai
,Aj
,Ak
, etc., are elements ofC
. Decide how to proceed based on the cardinality ofC′
:|C′| = 0
: No visible method definitions accept the supplied argument types. Name resolution has failed:N
is unresolvable.|C′| = 1
: Exactly one visible method definition accepts the supplied argument types, thereforeC′
is
Name resolution has succeeded:C′ ≝ {Ai}
.N
is resolved toAi
.|C′| = 2
: Multiple method definitions accept the supplied argument types, i.e., the available type information was insufficient to uniquely disambiguateN
. Name resolution has failed:N
is semantically ambiguous.
Consider the following example:
The module Summation
imports the name "_+_"
from each of Avail
and Dimensional Analysis
. Each of these modules separately defines an atom named "_+_"
. This module is nonetheless valid, because the resolution of "_+_"
to an atom is never ambiguous. On line 48, the arguments of "_+_"
are known to be numbers, which unambiguously identifies the method (and therefore the associated atom) to be the "_+_"
introduced and exported by Avail
. On line 56, each argument is a dimensioned quantity
, so the correct "_+_"
is the one introduced and exported by "Dimensional Analysis"
.
Now consider another (contrived) case, where the programmer wants to override "_+_"
with new behavior.
In Bad String Addition
, the method "_+_"
is overridden with a new definition, one sufficient to concatenate two strings. But which "_+_"
should be overridden? There are two in scope: one from Avail
and one from Dimensional Analysis
. In the previous example, the code was sending "_+_"
, so the types of the arguments were available to assist in name resolution. But in this example, no such information is available; the method definer, "Method_is_"
, has only the lexical name of the desired atom to guide it, and there are two visible atoms with the same lexical name. Compilation will therefore fail as a direct consequence of name resolution failure.
‹ Module Resolution | | | Return to Modules | | | Name Resolution › |