Declarations
A module is a header followed by declarations. A declaration is what puts a name into a module: a value, a function, a type, an operator, or a name borrowed from somewhere else.
There are five forms. Three of them are made of a type expression or a module name, and are specified alongside those:
| Form | Written | Specified in |
|---|---|---|
| import | import Widget exposing (Size) | Modules |
| type annotation | origin : Flag | Types |
| type declaration | type Flag = On | Off | Types |
| binding | origin = On, invert flag = … | below |
infix | infix left 6 (+) = add | below |
Every one of them begins in column 1, and a token in column 1 is what ends the declaration before it — see Layout.
Bindings
A binding gives a name to an expression. It is the name, then zero or more parameters,
then =, then the expression.
module Example exposing (Flag, origin, invert)
type Flag
= On
| Off
origin : Flag
origin =
On
invert : Flag -> Flag
invert flag =
case flag of
On ->
Off
Off ->
OnA value and a function are the same form, told apart only by whether any parameters were written, and no keyword marks one or the other. What a name is bound to is a question about its type, and the type is written down separately.
The body is an expression, and it may be written on the same line as the
= or on following lines, which must then be indented:
module Example exposing (Flag, origin)
type Flag
= On
origin : Flag
origin = OnThe name is a lowercase identifier
A binding's name is a lowercase-initial identifier — the same identifier rules every value name follows. An uppercase-initial name belongs to a type or a constructor, and writing one where a binding is expected is a syntax error:
module Example exposing (f)
Flag =
1An operator is never a binding's name either. (+) a b = a is not a declaration:
module Example exposing (add)
(+) a b =
aAn operator has no body of its own to bind. It is a name standing for a function that was
declared the ordinary way, and the infix declaration below is what
connects the two.
Declarations are unordered
The declarations of a module are all in scope throughout it, so a binding may name one written below it, and two bindings may name each other. Reordering a file's declarations never changes what it means.
module Example exposing (Flag, first)
type Flag
= On
first : Flag
first =
second
second =
OnParameters
Each parameter is a pattern, and the parameters
together are one binding position: a name may be bound only
once across all of them. Juxtaposition separates
one parameter from the next, which is why a constructor pattern with arguments is
parenthesised here and is not in a case branch.
A binding may name at most as many parameters as its annotation has arrows. Naming all of them and naming none are both ordinary:
module Example exposing (Flag, apply)
type Flag
= On
apply : Flag -> Flag -> Flag
apply =
swap
swap a b =
bNaming some of them is too. apply a below has one parameter against two arrows, and the
Flag -> Flag left over is what the body produces:
module Example exposing (Flag, apply)
type Flag
= On
apply : Flag -> Flag -> Flag
apply a =
swap a
swap a b =
bKnown gap: that block should be expect=ok. A binding with any parameters at all must
name exactly as many as the annotation has arrows, so the only two counts accepted today are
all of them and none (docs/tickets/lang-25.md).
Naming more parameters than the annotation has arrows is an error, and Types specifies it along with the rest of what an annotation and its declaration owe each other.
A binding has a body
An annotation on its own does not declare anything. A name that is annotated and never bound is an error at the annotation:
module Example exposing (Flag, f)
type Flag
= On
f : FlagA foreign facade is the one place this does not hold: a module foreign module
is annotations with no bodies, because the bodies are in the companion file.
Clauses
Not implemented: a binding may be written as several clauses, one per line, each with
its own parameters and its own body. The clauses share a name, and that is what makes them one
declaration (docs/tickets/lang-20.md).
module Example exposing (Flag, invert)
type Flag
= On
| Off
invert : Flag -> Flag
invert On = Off
invert Off = OnThe compiler parses the clauses and then reports that a name declared over more than one
binding is not supported, so a declaration has exactly one clause today and any pattern that
can fail has to go in a case.
Patterns specifies the pattern half
of the construct: the clauses are tried in the order written, they must cover the type between
them, and they are one binding position each.
The rules on a binding's name, its parameters and its body are all rules about a clause, and each holds whether a declaration has one clause or several.
Every clause names the same number of parameters
The clauses of one declaration are one function, and a function has one arity. Clauses that disagree about it describe no function at all:
module Example exposing (Flag, f)
type Flag
= On
| Off
f On = Off
f Off On = OnThe check is against the clauses themselves rather than against the annotation, so the block above is rejected although it has no annotation to disagree with.
The clauses stand together
A declaration's clauses are consecutive: nothing may come between them but blank lines and comments. A clause written apart from its siblings is an error.
module Example exposing (Flag, invert)
type Flag
= On
| Off
invert : Flag -> Flag
invert On =
Off
other =
On
invert Off =
OnKnown gap: that block is rejected today only because a declaration may not have more than
one clause at all. Clauses are gathered by name from anywhere in the module, so once several
are supported the block above would be accepted
(docs/tickets/lang-26.md).
Order among the clauses decides which one is tried first, so a clause is not something a reader can afford to find by searching.
infix declarations
An infix declaration binds an operator to a function and says how expressions written with
it group. It is the keyword infix, an associativity, a precedence, the operator in
parentheses, =, and the function's name.
module Example exposing ()
infix left 6 (+) = add
add a b =
aThe operator is then a name like any other, and a + b is add applied to a and b.
Nothing about any operator is built into the language; Expressions
specifies what one means, and Lexical
structure which spellings are available to be given one.
An infix declaration is unordered along with everything else: the operator may be used above
the line that declares it, and the function it names may be declared below.
Associativity
The associativity is one of three words — left, right or non — and it is written before
the precedence. The three are soft keywords: each is a
keyword in this one position and an ordinary name everywhere else. Writing them in the other
order is a syntax error:
module Example exposing ((+), add)
infix 6 left (+) = add
add a b =
aNeither part may be left out: there is no default associativity and no default precedence. An operator missing one would group differently in every expression that mixed it with a different neighbour.
module Example exposing ((+), add)
infix 6 (+) = add
add a b =
aWhat each associativity does to an expression is Expressions' subject.
Precedence is 0 through 9
A precedence is a single decimal digit: 0 binds loosest, 9 tightest. Ten levels is enough
to place every operator a program declares against the ones it imports.
module Example exposing ()
infix left 10 (^) = pow
pow a b =
aKnown gap: that block should be rejected — 10 is not a precedence. Any non-negative
integer up to 255 is accepted today (docs/tickets/lang-24.md).
A precedence of 300 is rejected:
module Example exposing ((^), pow)
infix left 300 (^) = pow
pow a b =
aKnown gap: that block should be rejected as a non-digit precedence; the error it gets today
names the range 0 to 255 instead, because the parser narrows the literal to a byte
(docs/tickets/lang-24.md).
A negative precedence is not a small precedence but a syntax error, since - is an
operator and not part of the literal that follows it:
module Example exposing ((^), pow)
infix left -1 (^) = pow
pow a b =
aThe function is declared in the same module
The name after the = is an unqualified lowercase name, and it must be one this module
declares. A name from another module is a syntax error rather than a lookup that fails:
module Example exposing ((+))
infix left 6 (+) = Other.addA name that is not declared anywhere is reported against the infix declaration:
module Example exposing (add)
infix left 6 (+) = plus
add a b =
aAn operator and the function behind it therefore live in one file, which is what lets every
other module name the operator alone: an importer's exposing ((+)) brings in a complete
meaning, with nothing left to resolve on its side. See
Modules for that end of it.
The function must take two arguments. That is a condition on its type rather than on how it
was written, so infix left 6 (+) = add is equally correct whether add names both of its
parameters, names one, or names none — all three are ways of writing a function of type
a -> b -> c.
Not implemented: the infix declaration is accepted whatever the function's type, and so
is every use of the operator, so a function of the wrong shape is caught nowhere
(docs/tickets/lang-28.md). No block above pins this, because no
phase rejects the declaration.
An operator has one infix declaration
A module declares each operator once. Two declarations for one operator give it two precedences and two meanings, and no rule chooses between them:
module Example exposing ()
infix left 6 (+) = add
infix right 7 (+) = mul
add a b =
a
mul a b =
aKnown gap: that block should be rejected. The declarations are collected into a map keyed
by the operator, so the second silently replaces the first and + is mul, right-associative
at 7 (docs/tickets/lang-27.md).
Where a declaration may appear
At the top level of a module, and nowhere else.
Not implemented: let … in introduces bindings local to an expression. The construct does
not exist yet; see Layout for what its declarations will be laid out
against.
A module foreign facade is the one module that restricts the list: it
may hold imports and annotations, and may not hold a binding, a type declaration or an
infix.