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:

FormWrittenSpecified in
importimport Widget exposing (Size)Modules
type annotationorigin : FlagTypes
type declarationtype Flag = On | OffTypes
bindingorigin = On, invert flag = …below
infixinfix left 6 (+) = addbelow

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.

expect=ok
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 ->
      On

A 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:

expect=ok
module Example exposing (Flag, origin)

type Flag
  = On

origin : Flag
origin = On

The 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:

expect=parse-error:UnexpectedToken
module Example exposing (f)

Flag =
  1

An operator is never a binding's name either. (+) a b = a is not a declaration:

expect=parse-error:UnexpectedToken
module Example exposing (add)

(+) a b =
  a

An 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.

expect=ok
module Example exposing (Flag, first)

type Flag
  = On

first : Flag
first =
  second

second =
  On

Parameters

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:

expect=ok
module Example exposing (Flag, apply)

type Flag
  = On

apply : Flag -> Flag -> Flag
apply =
  swap

swap a b =
  b

Naming 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:

expect=canonical-error:BindingPatternsInvalidLen
module Example exposing (Flag, apply)

type Flag
  = On

apply : Flag -> Flag -> Flag
apply a =
  swap a

swap a b =
  b

Known 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:

expect=canonical-error:NoBindings
module Example exposing (Flag, f)

type Flag
  = On

f : Flag

A 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).

expect=canonical-error:MultipleBindingsUnsupported
module Example exposing (Flag, invert)

type Flag
  = On
  | Off

invert : Flag -> Flag
invert On = Off
invert Off = On

The 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:

expect=canonical-error:BindingPatternsInvalidLen
module Example exposing (Flag, f)

type Flag
  = On
  | Off

f On = Off
f Off On = On

The 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.

expect=canonical-error:MultipleBindingsUnsupported
module Example exposing (Flag, invert)

type Flag
  = On
  | Off

invert : Flag -> Flag
invert On =
  Off

other =
  On

invert Off =
  On

Known 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.

expect=ok
module Example exposing ()

infix left 6 (+) = add

add a b =
  a

The 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:

expect=parse-error:UnexpectedToken
module Example exposing ((+), add)

infix 6 left (+) = add

add a b =
  a

Neither 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.

expect=parse-error:UnexpectedToken
module Example exposing ((+), add)

infix 6 (+) = add

add a b =
  a

What 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.

expect=ok
module Example exposing ()

infix left 10 (^) = pow

pow a b =
  a

Known 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:

expect=parse-error:InfixPrecedenceOutOfRange
module Example exposing ((^), pow)

infix left 300 (^) = pow

pow a b =
  a

Known 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:

expect=parse-error:UnexpectedToken
module Example exposing ((^), pow)

infix left -1 (^) = pow

pow a b =
  a

The 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:

expect=parse-error:UnexpectedToken
module Example exposing ((+))

infix left 6 (+) = Other.add

A name that is not declared anywhere is reported against the infix declaration:

expect=canonical-error:InfixReferenceInvalidValue
module Example exposing (add)

infix left 6 (+) = plus

add a b =
  a

An 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:

expect=ok
module Example exposing ()

infix left 6 (+) = add

infix right 7 (+) = mul

add a b =
  a

mul a b =
  a

Known 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.