This section of the manual attempts to provide a basic introduction to the
cedarbridge
language.
The syntax of the
cedarbridge language is based on
S-expressions, with each
cedarbridge
source file consisting of a series of statements structured as S-expressions.
A cedarbridge file should start with a
language statement
indicating which version of the cedarbridge language the file is targeting. Should any
backwards-incompatible changes be made to the language in the future, any files that explicitly declared which
version of the language they are targeting would continue to be compiled correctly without errors. A file that
does not contain a language statement will be compiled using whatever is the latest
version of the language the compiler supports. The following example specifies that the file is intended for
cedarbridge
major version 1, minor version 0:
The language allows for using parentheses or square brackets to enclose expressions as long as the use is
balanced. For example, all of these expressions are valid:
However, these expressions are not valid and will cause a parse error:
The
cedarbridge language contains a basic
package
system. All declared message types and protocols must be declared inside packages. A package has a unique name
conventionally in lowercase
reverse domain notation. A
package statement begins the definition of a
package, and the definition continues to the next
package statement or end-of-file,
whichever occurs first. The following statement begins defining a package
com.example:
Packages may import other packages using
import statements:
An import statement
(import x z) exposes all of
the types present in package x via the short name
z. For example, if a type T is declared in
x, the type can be referred to using the qualified name
z:T:
The cedarbridge language allows for defining message types based on algebraic sums and
products. Product types are referred to as record types (similar to
structs
in the C language), and sum types are referred to as
variant
types (similar to algebraic data types in Haskell or the ML family of languages). Types may be parameterized by
other types (referred to as
generics
or parametric polymorphism in other languages). A record type consists of a series of
uniquely-named fields, and the order of fields is significant. The following statement declares a simple
Color
record type consisting of red,
green, and blue fields:
The cedarbridge language supports conventional variant types.
The classic example for a variant type is the
option
type where values of type option may either be
None
or Some s for some value s. The following statement
declares the classic parameterized Option type, with the None
case having zero fields, and the
Some
case having exactly one field which is given the name
value:
In the
cedarbridge language, there are no backwards-compatible changes
possible for types: Adding, removing, or reordering record fields is a backwards-incompatible change.
Reordering the cases of a variant type, or changing the fields of cases are backwards-incompatible changes.
Rather than resort to the horrendously fragile and excessively permissive model used by other message
protocol languages such as
protobuf,
the
cedarbridge language exposes a strict and principled
versioning mechanism that allows for unambiguous reasoning about any given
version of a protocol; if you know the version of the protocol you are speaking, you know the exact
shape of any and all messages that appear in that protocol without the possibility of any extra or missing fields.
A
protocol declares a set of
versions, with each
version declaring the set of
types
present in each version of the protocol. The following example defines a simple protocol
Foo with three versions:
Note that Command0 and Command1 appear
in the first two versions of the protocol, but version 3 of the protocol drops support for
Command0. The Response type appears in
all versions of the protocol. This allows for a disciplined approach to versioning; if a newer version of a
protocol requires "changing" a message, the approach taken is to simply declare a new protocol version that
contains a new message type (and, almost certainly, removes the old message type).