It's useful to describe constraints about a program; arguably the most common way is using Types. Most languages differentiate between concepts like types, interfaces, abstract classes, traits, etc. In Elder, they're all just different ways to describe data using metadata.
The metadata relator : allows the developer to define whatever metadata constructs they find useful outside of what's defined within Elder syntax. It's used for both common concepts (like types) but can be extended to more nuanced constructs like constant-ness, object-capability, concurrency, or even business logic for a certain domain. Elder prefers that the problem decides the appropriate model instead of the reverse. This means Elder must allow the developer to extend their model to whatever they need to solve their problem. Since often this changes for different domains and use-cases, the model must be able to be customized.
For now, we'll start with the basics and leave the more complex type-like concepts for when we fully introduce compile time.
Reserved and Builtin
Elder reserves and specifies various types, literals, primitives, and constants as the use is generally so common that defying the conventions would lead to confusion.
Since we're starting with the most common concepts, notice that these are abstractions and don't represent the actual implementation. To keep it simple, more concepts will be introduced incrementally.
Not every type can be represented in every environment. If an implementation doesn't natively support the types there's a few ways to configure how the environment responds:
- When possible, emulate the type
- Default to a more general type
- Throw a compile time error on use
All that Elder syntax guarantees is that if it's defined, it must mean what is specified in Elder syntax standard. Not that it must be defined. For example, I32 (a 32 bit integer) doesn't have a native representation in JavaScript VM but it can be emulated using a bit of extra code the compiler can generate, default to a JavaScript builtin Number, or throw a comptime error on use.
Basic Types
Although there are many builtin types, let's start with the most common:
- Numeric
Numberrepresents any numberIntegralrepresents any integerDecimalrepresents any floating pointRatiorepresents any fixed point
- Container
Listrepresents a ordered series of itemsSetrepresents a unique series of itemsMaprepresents a series of key-value pairsStringrepresents a series of charactersOptionalrepresents a value which may not be present
- Logic
Boolonly can be valuesTrueorFalse
Literals Types
We follow the conventions of many languages for the most basic literals. Each literal uses the default implementation for the environment it's running in.
The most common are:
[is aListliteral- Often the default implementation is a
Array - Each sequence element is interpreted as a item of the list
- Terminates
- If inline, is terminated by closing
]or at end of line - If items are multiline, is implicity terminated at dedent
- If inline, is terminated by closing
- eg these are equivalent
list = [ 1, 2, 3 ] list = [ 1, 2, 3 list = [ 1 2 3
- Often the default implementation is a
{is aMapliteral- Often the default implementation is a
HashMap - Each sequence element is interpreted as a item of the list
- Key and value are separated by
= - If only key is present then ???
- Key and value are separated by
- Terminates
- If inline, is terminated by closing
}or at end of line - If items are multiline, is implicity terminated at dedent
- If inline, is terminated by closing
- eg these are equivalent
map = { a = 1, b = 2, c = 3 } map = { a = 1, b = 2, c = 3 map = { a = 1 b = 2 c = 3
- Often the default implementation is a
"is a inlineString- Every character after starting quote is it's content including whitespace
- The escape character
\works as expected - Terminates
- Since inline, is terminated by closing
"or at end of line
- Since inline, is terminated by closing
- eg these are equivalent
str = "my long name" str = "my long name - edge-cases
- sequence of string?
str = "my" "long" "name" - concat? w/ ` ` or
\nstr = " my long name- concat w/ `` (empty)
str = "mylongname" - concat w/ ` ` (space)
str = "my long name" - concat w/
\n(newline)str = "my\nlong\nname"
- concat w/ `` (empty)
- sequence of string?
"""is a blockString- The characters of it's content ignore the leading space of a 2-space indent
- The escape character
\works as expected - Terminates
- Since it multiline, is terminated by dedent
- eg these are equivalent
str = """ my multil name str = "my\nlong\nname" - edge-cases?
str = """ my multi name """ my other name
Literal Values
By default there are values which are interpreted as known types to avoid unnecessary visual noise like:
- Numeric
- Decimal
1.0 - Integral
4 - Ratio
3/4 - Hex
0xF00D - Binary
0b1010 - Octal
0o744
- Decimal
- Logic
Truefor logical true,Falsefor logical false- Notice these looks like Types and will be explained later once comptime is introduced
- There aren't any implicit conversion to boolean
Types
Syntactically a type must start with a alphabetic, upper-case character and each new word of it's name should be upper-case like MyTypeName.
Type Sugar
Types get special treatement as they're so widely used and useful.
Lets start simple with an example:
x:(type = Integral) = 4
Notice that x's type is literally represented by the key type under the metadata relator :. The key :type, among others, has a reserved meaning in Elder syntax to mean the logical type of the name it's describing.
Since defining types is so common, we support sugar to minimize the syntactical noise:
x:Integral = 4
This only works for identifiers which can be interpreted as a types (meaning starting with upper-case alphabetic character) and is under the metadata relator :.
It's still possible to add more metadata:
x:(Integral, min = 0, max = 10) = 4
or the equivalent expanded form:
x:(type = Integral, min = 0, max = 10) = 4
It is a syntax error to set both the type and use the shorthand together like:
x:(Integral, type = Decimal) = 4
Emulate Serialization Languages
Using the syntax so far we have enough to emulate various serialization languages.
JSON is ubiquitous so let's see the difference.
Consider a somewhat simple JSON example:
[
{
"department": "History",
"staff": [
{
"name": "Jane Janison",
"is-tenured": true
},
{
"name": "Billy Bane",
},
{
"name": "Katie Kattson",
"is-assistant": true
},
]
},
{
"department": "Mathematics",
"staff": [],
},
{
"department": "Biology",
"staff": [
{
"name": "Dirk Darwin",
"specialization": "Evolutionary",
"teacher-assistants": [
]
},
{
"name": "Ellen Erickson",
"teacher-assistants": [
{
"name": "John Johnson"
}
]
},
],
},
We can recreate with Elder:
[
- department = History
staff = [
- name = Jane Janison
is-tenured = True
- name = Billy Bane
- name = Katie Kattson
is-assistant = True
- department = Mathematics
staff = []
- department = Biology
staff = [
- name = Dirk Darwin
specialization = Evolutionary
teacher-assistants = []
- name = Ellen Erickson
teacher-assistants = [
name = John Johnson
It's not perfect as there's still some repetition in the shape and structure of the data. Later on it can be simplified further.