3. Phrases
A phrase is a sentence pattern of words and typed holes, defined once and then used like a built-in. Everything except the language words and forms is a phrase, including the whole standard library.
define Send [m: msg] to [n: node]
emit m by n
3.1 The pattern rules
Taught in 7. Phrases
- A pattern contains at least one word. Its first word is the phrase's head.
- Receiver. A pattern may start with one hole before its head, the
receiver. Its type is a node type,
Nodeorp.Node:
A phrase with a receiver is a receiver phrase. It is node-context code (define [n: Node] Learn [k: node] at [v: number]spec/05) and is called with its receiver joined to its head by.:n.Learn k at v. Every other hole comes after the head. - No two inline holes are adjacent.
[a: number] plus [b: number]is fine;[a: number] [b: number]is not. - Every hole is typed, element types included:
[n: node],[ns: list of Node]. Never[n]or[ns: list]. - Block holes come last. A hole of type
blockis filled by an indented block, not inline. At least one word or inline hole stands between the head and the first block hole. After a block hole come only further pairs of one word and one block hole; each such word starts a continuation line (spec/02§2.1). This is howif … elseis built:
used asdefine Retry [n: number] times [b: block] otherwise [c: block]Retry 3 times net.Send request from n to server otherwise emit (message GaveUp with Tries 3) by n - The head carries the export capital and, outside its package, the
qualifier:
net.Send m to n,n.gossip.Learn k at v,look.Fill card with theme.Accent. - Reserved words (
spec/01) are never heads and may appear as later words:Send [m: msg] to [n: node],Majority of [n: number]. Words after the head start lower-case ([n: node] to, neverTo); only the head carries the export capital. - Hole types name known types. An unknown type, such as
[x: Foo]orp.Nodewherepis not an imported library package, is abad-patternerror. - A head is never directly followed by a comparison operator (
is,is not,is in,is not in,less than,more than,at least,at most):Count is [n: number]is abad-patternerror, because its sentences read as comparisons andnot (…)could not be rewritten without changing their meaning.
3.2 Names and uniqueness
- A phrase's name is its word skeleton, with the receiver and holes removed:
Learn … at. - Within a package every name is defined once. There is no overloading by hole types, by number of holes, or by having a receiver.
- One namespace per package holds its phrases, its record types, its
node type
Nodeand its node fields. Record fields are scoped to their record type. Nodeis the node type's name in every package, so no phrase head, record type or field in any package may be namedNode(duplicate-name). Likewise a record type may not be named like a built-in type (text,number,bool,time,node,msg,shape,color,field,package,block,value;duplicate-name).- A package must not define a phrase whose skeleton equals a language form's skeleton.
3.3 Resolution
Taught in 7. Phrases
Each sentence is resolved independently.
- Candidates come from the sentence's first item:
- the language forms (
spec/02§2.2); - a bare word
H: the current package's phrases without a receiver whose head isH; - a member
p.H, wherepis an imported package:p's exported phrases without a receiver whose head isH; - a member
x.Horx.p.H, wherexnames a node value: the receiver phrases with headHof the current package, or the exported ones ofp.
- the language forms (
- Match. A pattern matches by shape: the sentence has the same
number of items, literal words agree position by position, and the
block structure agrees (a block hole is filled by a block, each
continuation word equal). Types never select between phrases. Once
exactly one pattern matches, each hole's item must have an accepted
type (
spec/04§4.7); a failure is atype-mismatcherror, not anunknown-phraseerror. For a receiver phrase the first itemx.H(orx.p.H) stands for the receiver and the head together:xfills the receiver hole and the remaining items match the rest of the pattern.n.Learn k at vmatches[n: Node] Learn [k: node] at [v: number]. Because a hole always takes exactly one item, there is no partial or longest match. - Result. Exactly one candidate must match by shape. Zero is an
unknown-phraseerror; two is anambiguous-phraseerror that names both definitions, even if their hole types differ. - Event phrases are candidates only after
when, and awhentakes only event phrases; a miss either way is anunknown-phraseerror. - The attachment
w of x(spec/09§9.2) is a candidate only whenwis a bare word that is not the head of a phrase of the current package, soMajority of ncalls the phrase.
Names
A bare word in an argument position is a name, never a literal word of the pattern. A name is exactly one of:
- a block local or an
eachname; - a name bound by the enclosing
when, its receiver included; - a hole parameter of the enclosing
define, its receiver included; - a zero-hole phrase without a receiver of the current package;
- a package name: an import, or the current package's own name as a
packagevalue.
Two of these with the same spelling in scope is a shadowing error, so
every name has one meaning. This includes a local, binding, hole or
each name spelled like a record type of the current package, and a
node field or phrase head spelled like one of the file's import
qualifiers (n.gossip would be ambiguous). Imports are scoped to their
file; uses, running and cycles are per package. Inside a when event phrase, a bare word that
is not yet a name binds a new one (spec/05 §5.6). In a field hole, a
bare word names a node field of the current package; it does not read it.
Members
A member is read from left to right. Its first word is a name. Each following word is a named member of what comes before it:
Before . |
After . |
Examples |
|---|---|---|
a package p |
an exported phrase head, record type or Node of p; in a field hole, an exported node field of p |
net.Send, api.Get, gossip.Node, raft.Role |
a record value of type R |
a field of R |
Vote.term, best.stamp, h.mutation |
a node value of type Node or q.Node |
a node field or receiver phrase head of the current package; or a package p the node runs, then an exported node field or receiver phrase head of p |
n.Known, n.Version, n.gossip.Known, server.tcp.State |
- A
whenthat matches a qualified record type binds the record under that spelling, soapi.Getnames the record value andapi.Get.Keyreads its field. - A value of type
nodehas no members.msgvalues have no members. - A member that ends at a field is a value, and can also be a place for
set. A member that ends at a phrase head is a call.
3.4 Results
Taught in 7. Phrases
A phrase either performs actions or produces a value.
- A value phrase gives its value with
result.resultends the phrase. The compiler checks that every path through a value phrase reaches aresult(missing-result), and that all results have one type: their join (spec/04§4.6). - An action phrase has no
result. - A value phrase may also have effects (for example
NewRequestin the Cassandra example updates a counter); the distinction is only about whether it gives a value. - A value phrase used as a statement, or an action phrase used as an argument, is an error.
resultappears only directly in a phrase definition, where the phrase is still running. It is acontexterror indefine main,start,whenoralways, and inside anafterblock or a block argument (for example atime.Everyblock), because that block runs after the phrase has returned. A value phrase is one whose body containsresult.
define Majority of [n: number]
result ((math.Floor (n divided by 2)) plus 1)
3.5 Block holes
Taught in 10. Faults
Inside a define, the name of a block hole used alone as a statement
runs the block. The block runs in the caller's context and sees the
caller's names as they were when the phrase was called. It holds a copy
of the caller's locals, so setting one of them inside the block is a
bad-place error. In node code the
block sees the receiver: inside define [n: Node] start, a time.Every 1s
block uses n.
define Every [d: time] [b: block]
b
after d
Every d
b
A block hole is filled only by an indented block, never by an argument:
the name b is never passed inline, and Every d above is filled with
a block that runs b.
3.6 Native phrases
Taught in 7. Phrases
define <pattern> native "<id>" binds a pattern to a primitive
implemented by the runtime. Only packages under std/ may declare native
phrases. The id is namespaced: "net.send", "look.tween". Natives
have the same pattern rules and the same resolution as ordinary phrases.
A std phrase has the contexts listed in the Context column of
std/README.md and is usable only there; the compiler enforces that
column at every call site of a std phrase, native or written in
Synod.
Native event phrases (used after when) are declared the same way
and are tagged by the runtime as either a node event or a trace event
(see spec/05 and spec/08).