### Declarations & Declarators

There is no "top level" in Pinafore. A program file consists of an expression. A module consists of a list of declarations.

#### Declarators

Declarators output bindings to be used in the scope of declarations and expressions.

##### "Let" Declarators

`let`-declarators output bindings from their contained declarations. Declarations in `let`-declarators have sequential, not recursive scope.

For example:

``````let a=3; b=4 in a+b
``````

Here `a+b` is an expression, `a=3` and `b=4` are declarations, and `let a=3; b=4` is a declarator. This declarator outputs bindings for `a` and `b` to the scope of the expression `a+b`.

Declarators can also output bindings to declarations:

``````let
let a=3; b=4 in c=a+b;
in c+1
``````

Here the declarator `let a=3; b=4` outputs bindings for `a` and `b` to the declaration `c=a+b`.

The declarator `let let a=3; b=4 in c=a+b` outputs a binding for `c`, but not `a` or `b`, to the expression `c+1`.

A declarator can also be converted into a declaration using the `end` keyword:

``````let
let a=3; b=4 end;
c=a+b;
in c+a+b
``````

Here `let a=3; b=4 end` and `c=a+b` are declarations. The outer declarator outputs bindings for `a`, `b`, and `c` to the expression `c+a+b`.

##### Recursive "Let" Declarators

For recursive declarations, use `let rec` to make a recursive "let"-declarator. Declarator declarations and namespace declarations are not allowed inside recursive blocks.

``````let

let rec
datatype P of
Mk (Q -> P);
end;

datatype Q of
Mk (P -> Maybe Q);
end;
end;

let rec
fact = match
0 => 1;
n => n * fact (n - 1);
end;
end;

in fact 12
``````
##### "With" Declarators

"With" declarators copy bindings from a given namespace into the current namespace. See Namespaces below.

##### "Import" Declarators

"Import" declarators import bindings from a module, which can either be one of the standard libraries, or a module file.

#### Declarations

These are the different kinds of declarations:

• Value declarations
• Type declarations
• Subtype declarations
• Standalone declarator declarations
• Declarator-qualified declarations
• Namespace declarations
• Expose declarations
##### Value Declarations

Value declarations are of the form `<pattern> = <expression>`.

###### Infix Operators

New operators can be declared with parentheses, like this:

``````let
(&\$\$&) = fn a,b => a - b;
in 57 &\$\$& 22
``````

The parsing "infixity" of the operator is determined by its name (regardless of namespace) according to the table, and is "(A x B) x C" level 10 for other names.

See Types.

##### Standalone Declarator Declarations

Any declarator can be turned into a declaration by appending `end`.

``````let

let rec
x = y;
y = 4;
end;

namespace N of
z = 1;
end;

with N end;

in x + y + z;
``````
##### Declarator-Qualified Declarations

Declarators can qualify declarations just as they can qualify expressions.

``````let

namespace N of
z = 1;
end;

with N in x = z;

in z;
``````
##### Namespaces & Namespace Declarations

A namespace is a space for declarations. Each namespace is either the root namespace, or a namespace with a name within another namespace. This name starts with an upper-case letter. Thus, each namespace can be identified by a list of zero or more names.

In a given scope, all declaration names are in some namespace. The full name of a declaration consists of the name and the namespace. Full names are unique in the scope.

Note that name qualification goes in order specific-to-general, the reverse of most other languages. So variable `x` inside namespace `N` inside namespace `M` is `x.N.M.`.

###### Referring to Declarations

In a given scope, there is a current namespace.

References to namespaces are either relative or absolute. Absolute namespace references consist of names separated by dots, with a trailing dot. These names specify the namespace directly.

Relative namespace references consist of names separated by dots. These are searched in the current namespace, and then all ancestors of the current namespace.

For example,

• `namespace Metadata.Image.` refers to the namespace `Metadata` within the namespace `Image` within the root namespace.
• `namespace Metadata.Image` refers to the namespace `Metadata` within the namespace `Image` within searched namespaces.

Likewise, references to full names are either relative or absolute. Absolute full name references consist of names separated by dots, with a trailing dot. These names specify the namespace directly, and then the name within the namespace.

Relative full name references consist of names separated by dots. These names are searched in the current namespace, followed by all ancestor namespaces.

For example, if the current namespace is `B.A.`:

• `async.Task.` is always `async.Task.`, that is, it refers to the declaration `async` within the namespace `Task` within the root namespace.
• `async.Task` refers to the first found of these: `async.Task.B.A.`, `async.Task.A.`, `async.Task.`.
###### Current Namespace

All declarations are placed within the current namespace.

A `namespace` declaration specifies the current namespace for the declarations it contains, relative to the existing current namespace.

###### Mapping Namespaces

A `with` declarator aliases names into different namespaces. For example:

• `with P` maps the contents of namespace `P` into the current namespace.
• `with P (a,b)` maps `a.P` and `b.P` into the current namespace
• `with Q.P` maps the contents of namespace `Q.P` into the current namespace.
• `with P (namespace Q)` maps namespace `Q.P` into the current namespace as `Q`.
• `with P (a,b) as N` maps `a.P` and `b.P` into namespace `N`, so they can be referred to as `a.N` and `b.N`.
###### Example
``````let

namespace A of
p = 3;

namespace B of
q = 4;
end;

end;

namespace C of
s = 6;
with B.A. end;
t = q + 12;
end;

with A end;

s = p + q.B + s.C.A;

in body
``````

In this example, the scope for `body` contains declarations with these full names, with these values:

``````p.A. = 3
q.B.A. = 4
s.C.A. = 6
t.C.A. = 16
p. = 3
q.B. = 4
s. = 13
``````

At the point at which `q.B.A.` is declared, references such as `x` will be searched in this order:

• `x.B.A.` (the current namespace)
• `x.A.` (parent of the above)
• `x.` (parent of the above)
##### Expose Declarations

Expose declarations provide a simple way of hiding declarations. Only the specified names will be exposed from the declaration, although subtype relations (which are nameless) will always be exposed. Expose declarations can be used within `let`-expressions for more fine-grained hiding.

``````let

let
# Mk.LowerCaseText not exposed
datatype LowerCaseText of Mk Text end;

subtype LowerCaseText <: Text = fn Mk.LowerCaseText t => t;

toLowerCase: Text -> LowerCaseText;
toLowerCase t = Mk.LowerCaseText \$ textLowerCase t;
in expose LowerCaseText, toLowerCase;

# Outside the above block, there is no way to create a LowerCaseText
# that is not lower-case text.

in toLowerCase "Hello"
``````

It's also possible to expose everything in a namespace:

``````let

let
x = 1; # not exposed

namespace N of
p = x + 2;
q = 3;
end;

r = 4;
in expose namespace N, r;

in N.p + N.q + r
``````