Flesh out details on basic stuff

This commit is contained in:
joeduffy 2016-12-16 13:35:59 -08:00
parent 97f5f54fd0
commit 271d30e895

View file

@ -42,7 +42,37 @@ most notably its unique combination of metadata-orientation, strong typing, decl
### Formatting
### Comments
A few brief words on formatting are noteworthy, since they will show up throughout this document.
#### Punctuation and Whitespace
In order to place an emphasis on brevity, further encourage declarative patterns, and following the lead of newer
languages, Mull elides needless C-style punctuation in two specific places.
First, semicolons `;` are not required to terminate statements or declarations.
var three = 1 + 2
Second, commas are not required to separate property values when creating values, maps, and so on.
var map = {
"a": 42
"b": 99
}
In the latter case, it is possible to write single-line initializers, in which case commas are still necessary.
var map = { "a": 42, "b": 99 }
In both cases, punctuation is still legal in the grammar, however good Mull style omits it.
var three = 1 + 2;
var map = {
"a": 42,
"b": 99,
}
#### Comments
Mull provides C-style `/* */` block comments in addition to C++-style `//` line comments.
@ -146,8 +176,20 @@ To break the tie, an alternative name can be given.
In this example, elements may be accessed using the `awshelpers` and `helpers` prefixes, respectively.
TODO: consider adding a shortcut for importing many things at once. E.g.
import aws/*
There's a question of whether we'd still want to refer to types by module name, e.g. `ec2.Instance` versus just
`Instance`. This impacts naming guidelines. Best to optimize for "cut and paste," however.
For information on how imports are resolved, please see [the dependencies document](deps.md).
#### Exports
To export a top-level type from a module, capitalize it. This applies to types, functions, and constants.
service foo {} // private, not exported
service Bar {} // public, available to consumers
### Types
Mull features a mostly-nominal type system. Given that the language is largely about dealing with data of different
@ -255,12 +297,26 @@ Schema types may give aliases to other structured schema types.
Given this definition, we can use `Employees` as a type alias anywhere for an array of `Employee` schema values.
Finally, schema types can be strongly typed enums, constraining the value space to what is listed. To do this, use an
alias but list the valid values using the or '|' delimiter. All possible values must be of the same type.
Mull supports the concept of union and literal types in place of enums. This allows more concise expression of common
patterns in declarative specifications. For example, to declare the set of states:
schema Address {
// as above ...
state: "AL" | "AK" | ... | "WI" | "WY"
}
The union expression may instead be given a name using aliases for easier readability and/or reuse.
schema State = "AL" | "AK" | ... | "WI" | "WY"
TODO: numeric enums? named key/value enums?
Finally, Mull also supports the concept of intersection types, allowing easy combination of multiple types into one.
schema A ...
schema B ...
schema AplusB = A & B
The resulting type `AplusB` contains the entire set of properties from both `A` and `B`. As a result, instances can be
freely structurally converted in place where `A` or `B` values are required, without explicit conversions.
#### Service Types
@ -298,7 +354,8 @@ A `properties` block defines a schema attached to this service instance, describ
optional persistent: bool
}
Any instantiation of a service will need to provide these properties.
Any instantiation of a service will need to provide these properties. These properties are also publically available
on the service instance post-construction, although they cannot be mutated afterwards.
A `new()` block creates any services encapsulated by this service, typically using the properties as input.
@ -307,13 +364,17 @@ A `new()` block creates any services encapsulated by this service, typically usi
}
By default, services created within this block are private implementation details of the enclosing service definition.
It is possible to export instances for public usage, however. To do so, list it in the `exports` block:
It is possible to export instances for public usage, however. To do so, list it in the `outputs` block:
exports {
new() {
table = new mu.Table {}
}
outputs {
table: mu.Table
}
All exported services must be definitely assigned inside of the `new()` function block.
After constructing a service with outputs, they will be available for read access by callers. Note that output
variables can be of any type, not just services. Each must be definitely assigned inside of the `new()` function block.
Although it isn't stated in the source code, `new()` is a function. Functions are explained later on, however,
these are computations evaluated at "compile-time", but not deployed and run in the cloud runtime environment. As a
@ -362,44 +423,214 @@ types may appear). The input is used to control the subscription and output is
semantics of how frequently an event fires, whether it is single-shot or repeating, and so on, are part of the semantics
of the event itself and not specified by the system. The system does, however, specify what it means to unsubscribe.
### Values, Variables, and Constants
TODO: constructing new values (including maps, arrays, etc).
TODO: string interpolation.
var
const
### Names
Identifiers
Accessibility
Conventions
### Storage
There are two kinds of storage locations.
The first are immutable constants. These are indicated by the `<ident> := <expr>` declaration.
cidr := "0.0.0.0/24"
service A {
new() {
subnetCIDRs := [ "10.0.1.0/24", "10.0.2.0/24" ]
}
}
A constant may not be reassigned after being declared and names may not be shadowed in any way. Notice that constants
can appear at the module-level -- including being exported -- or inside of a function. A constant at the module-level
cannot be of a service type, since only service constructors are permitted to allocate new services.
The second are mutable variables. These are indicated by the `var <ident>` declaration and may not appear at the
module-level, since then modules would be stateful, and things like import order would matter, opening the door to
non-determinism. Such a statement may optionally have an initializer for its initial value, as in
`var <ident> = <expr>`. If a declaration does not have an initializer, it must be followed by a type annotation, as in
`var <ident>: <type>`. Note that a declaration may have both a type annotation and an initializer, as in
`var <ident>: <type> = <expr>`, although the type annotation is optional in this case as the compiler will perform
local type inference to assign a type otherwise.
### Values
There are a few different ways to construct new values in Mull, depending on the target type. The primitive types are
all literal-based while the other types require other forms of creation.
#### Booleans
Each `bool` value has the literal value `true` or `false`.
#### Numbers
Because Mull, like JavaScript, has a single number type to represent both integers and floating-point numbers, numeric
literals can take many forms.
Integer literals are sequences of digits, with an optional prefix. By default a number is base-10, however the `0x`
prefix may be used to specify hexadecimal numbers in base-16 (digits `0-9a-z`), `0o` to specify octal numbers in base-7
(digits `0-7`), or `0b` to specify binary numbers in base-2 (digits `0-1`). For example:
42
1701483783280928
0o600
0xcafebeef
0b11011011000110
Floating-point literals have an integer part, a decimal point `.`, a fractional part, and an exponent part. The integer
and fractional part represent the decimal digits, for example `72.33`, while the exponent part is an `e` or `E` followed
by an optionally signed decimal exponent, for example `E+5`. One of the integer or fractional part may be elided; one
of the decimal point or the exponent may be elided. For example:
0
72.40
072.40
2.71828
1.E+0
6.67428E-11
1E6
.25
.12345E+5
TODO: JavaScript stores numbers as IEEE 754 and hence can't support more than 2^53-1. So traditional 64-bit longs are
out. This seems like a problem and so we should consider supporting ints, longs, and/or maybe just bignums.
#### Strings
A string literal represents a UTF-8 encoded string. There are two forms of literals.
The first is a raw string literal, enclosed in double quotes `"`. It may contain any UTF8 codepoint sequence. As is
typical in C-like languages, however, the following special characters may also be embedded using an escape `\\`:
`\"` (double quote), `\\` (backslash), `\a` (bell), `\b` (backspace), `\f` (new page), `\n` (newline), `\r` (carriage
return), `\t` (horizontal tab), `\v` (vertical tab), `\nnn` (octal character value), `\xnn` (hexadecimal character
value), `\unnnn` (universal code point `U+nnnn`), `\Unnnnnnnn` (universal code point `U+nnnnnnnn`).
"abc"
"\n"
"Hello, world!\n"
"日本語"
"\u65e5本\U00008a9e"
"\xff\u00FF"
The second form is an interpolated string literal, enclosed in backticks ````. These literals may span newlines and
may include embedded expressions using the sequence `${expr}`. In these cases, the string contents are replaced by the
value resulting from evaluating `expr`, which must be convertible to a string.
`abc`
`
` // same as "\n"
`\n` // same as "\\n"
`Hello, ${name}!`
Both forms may be concatenated using the `+` character. Strings separated only by newlines and spaces are automatically
concatenated together. For example, this:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n" +
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n" +
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n"
is the same as this:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\n"
"tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\n"
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n"
Both are equivalent to using the interpolated string form, but permits explicit injection of newlines and formatting:
`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.`
#### Arrays
An new array is created by enclosing elements within `[` and `]` tokens.
mems := [ 128, 64, 512, 1024*8, 8 ]
disks := [
4
2
1
4
0
]
labels := [ "web", "registry", "db", "discovery" ]
instances := [
"t2.nano"
"t2.micro"
"t2.small"
"t2.medium"
"t2.large"
"t2.xlarge"
"t2.2xlarge"
]
Notice that we can elide the commas `,` as per the earlier discussion of punctuation.
The most common type amongst all elements will be assumed as the resulting array's element type. So, for example, an
array of numbers *and* strings, will yield an `any[]`. If the elements do not share a common base type -- as would be
the case for a mixture of schema and service types -- the compiler will produce an error.
To explicitly produce an array with specific element types, you may use the `new` keyword to explicitly state one:
tags := new tag[] {
{ key: "Name", value: name }
{ key: "Kind", value: kind }
}
If any elements cannot be converted to the target type -- in this case `tag` -- an error will occur.
#### Maps
A new map is created by enclosing elements within `{` and `}` tokens.
zoneCounts := {
"us-east-1": 5
"us-east-2": 3
"us-west-1": 3
"us-west-2": 3
"ca-central-1": 2
}
A map may be keyed by `number` or `string`s.
TODO: discuss omission of quotes for keys.
TODO: talk about explicit typing.
TODO: custom key types if schema types can have stringification functions.
#### Custom Schema Types
#### Services
#### Default Values
### Functions
#### Built-In Macros
There are a plethora of built-in macros.
An entry in a map can be deleted entirely using the `delete` function:
An entry in a map can be deleted entirely using the `map.Delete` function:
import mu/map
var m = map<string, int> {
"a" = 1
"b" = 2
}
delete(m, "a")
map.Delete(m, "a")
// At this point, m is just {
// "b" = 2
// }
### Expressions
TODO: array and map initialization.
TODO: arithmetic.
## Runtime Bindings
@ -448,6 +679,8 @@ purposes -- they are not meant to be edited by hand. Doing so can cause corrupt
TODO: specify the full graph state format and contents.
TODO: import from AWS.
## Language Specification
TODO: a formal specification of the language and its grammar.