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

  1. A pattern contains at least one word. Its first word is the phrase's head.
  2. Receiver. A pattern may start with one hole before its head, the receiver. Its type is a node type, Node or p.Node:
    define [n: Node] Learn [k: node] at [v: number]
    
    A phrase with a receiver is a receiver phrase. It is node-context code (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.
  3. No two inline holes are adjacent. [a: number] plus [b: number] is fine; [a: number] [b: number] is not.
  4. Every hole is typed, element types included: [n: node], [ns: list of Node]. Never [n] or [ns: list].
  5. Block holes come last. A hole of type block is 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 how if … else is built:
    define Retry [n: number] times [b: block] otherwise [c: block]
    
    used as
    Retry 3 times
      net.Send request from n to server
    otherwise
      emit (message GaveUp with Tries 3) by n
    
  6. 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.
  7. 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, never To); only the head carries the export capital.
  8. Hole types name known types. An unknown type, such as [x: Foo] or p.Node where p is not an imported library package, is a bad-pattern error.
  9. 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 a bad-pattern error, because its sentences read as comparisons and not (…) could not be rewritten without changing their meaning.

3.2 Names and uniqueness

3.3 Resolution

Taught in 7. Phrases

Each sentence is resolved independently.

  1. 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 is H;
    • a member p.H, where p is an imported package: p's exported phrases without a receiver whose head is H;
    • a member x.H or x.p.H, where x names a node value: the receiver phrases with head H of the current package, or the exported ones of p.
  2. 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 a type-mismatch error, not an unknown-phrase error. For a receiver phrase the first item x.H (or x.p.H) stands for the receiver and the head together: x fills the receiver hole and the remaining items match the rest of the pattern. n.Learn k at v matches [n: Node] Learn [k: node] at [v: number]. Because a hole always takes exactly one item, there is no partial or longest match.
  3. Result. Exactly one candidate must match by shape. Zero is an unknown-phrase error; two is an ambiguous-phrase error that names both definitions, even if their hole types differ.
  4. Event phrases are candidates only after when, and a when takes only event phrases; a miss either way is an unknown-phrase error.
  5. The attachment w of x (spec/09 §9.2) is a candidate only when w is a bare word that is not the head of a phrase of the current package, so Majority of n calls 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:

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

3.4 Results

Taught in 7. Phrases

A phrase either performs actions or produces a value.

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