Closed chharvey closed 1 year ago
This update includes support for constant types — the equivalent to constant collections but for types. It includes non-breaking changes.
A contant collection type is a constant tuple or record type. It has the same syntax as a constant tuple/record: It is delimited by \[
and ]
, only allows constant type literals (unit types and never
, void
, null
, bool
, int
, float
, str
, obj
, and unknown
), type operations thereof, and nested constant collection types. It does not allow type identifiers, regular tuple/record literal types, list/dict/map/set literal types, generic calls, or the mutable
operator.
type ConstTupleType = \[int, float, str];
type ConstTupleTypeShorthand = int\[3];
type ConstRecordType = \[a: int | float, b: str & obj, c: unknown];
type SyntaxErr = int\[]; % SyntaxError: tuple type shorthand must have an integer
type SyntaxErrs = \[
T, % SyntaxError: cannot contain identifiers
[int, float, str], % SyntaxError: cannot contain variable collections
[a: int, b: float, c: str], % SyntaxError: cannot contain variable collections
T.<S>, % SyntaxError: cannot contain generic calls
];
Semantically, the mutable
operator is invalid on constant collection types.
type SemanticErr = mutable int\[3]; % TypeError: invalid operation
Constant collection types may have optional entries.
% still allowed:
let a: [int, int, ?: int] = \[42, 420];
let b: [n42?: int, n420: int] = \[
n420= 420,
];
% new: allowed:
let c: \[int, int, ?: int] = \[42, 420];
let d: \[n42?: int, n420: int] = \[
n420= 420,
];
-EntryType<Named, Optional> ::= <Named+>(Word . <Optional->":") <Optional+>"?:" Type;
+EntryType<Named, Optional, Variable> ::= <Named+>(Word . <Optional->":") <Optional+>"?:" Type<?Variable>;
-ItemsType ::=
+ItemsType<Variable> ::=
- | EntryType<-Named><-Optional># ","?
- | (EntryType<-Named><-Optional># ",")? EntryType<-Named><+Optional># ","?
+ | EntryType<-Named><-Optional><?Variable># ","?
+ | (EntryType<-Named><-Optional><?Variable># ",")? EntryType<-Named><+Optional><?Variable># ","?
;
-PropertiesType ::= EntryType<+Named><-Optional, +Optional># ","?;
+PropertiesType<Variable> ::= EntryType<+Named><-Optional, +Optional><?Variable># ","?;
-TypeGrouped ::= "(" Type ")";
-TypeTupleLiteral ::= "[" (","? ItemsType)? "]";
-TypeRecordLiteral ::= "[" ","? PropertiesType "]";
-TypeDictLiteral ::= "[" ":" Type "]";
-TypeMapLiteral ::= "{" Type "->" Type "}";
-GenericArguments ::= "<" ","? Type# ","? ">";
+TypeGrouped <Variable> ::= "(" Type<?Variable> ")";
+TypeTupleLiteral <Variable> ::= <Variable->"\[" <Variable+>"[" (","? ItemsType<?Variable>)? "]";
+TypeRecordLiteral <Variable> ::= <Variable->"\[" <Variable+>"[" ","? PropertiesType<?Variable> "]";
+TypeDictLiteral ::= "[" ":" Type<+Variable> "]";
+TypeMapLiteral ::= "{" Type<+Variable> "->" Type<+Variable> "}";
+GenericArguments ::= "<" ","? Type<+Variable># ","? ">";
-TypeUnit ::=
+TypeUnit<Variable> ::=
| KEYWORD_TYPE
- | IDENTIFIER
+ | <Variable+>IDENTIFIER
| PrimitiveLiteral
- | TypeGrouped
- | TypeTupleLiteral
- | TypeRecordLiteral
- | TypeDictLiteral
- | TypeMapLiteral
+ | TypeGrouped <?Variable>
+ | TypeTupleLiteral <-Variable>
+ | TypeRecordLiteral <-Variable>
+ | <Variable+>TypeTupleLiteral <?Variable>
+ | <Variable+>TypeRecordLiteral <?Variable>
+ | <Variable+>TypeDictLiteral
+ | <Variable+>TypeMapLiteral
;
PropertyAccessType ::= "." (INTEGER | Word);
GenericCall ::= "." GenericArguments;
-TypeCompound ::=
- | TypeUnit
- | TypeCompound (PropertyAccessType | GenericCall)
+TypeCompound<Variable> ::=
+ | TypeUnit<?Variable>
+ | TypeCompound<?Variable> PropertyAccessType
+ | <Variable+>(TypeCompound<?Variable> GenericCall)
;
-TypeUnarySymbol ::=
- | TypeCompound
- | TypeUnarySymbol (
+TypeUnarySymbol<Variable> ::=
+ | TypeCompound<?Variable>
+ | TypeUnarySymbol<?Variable> (
| "?"
| "!"
- | "[" INTEGER? "]"
- | "{" "}"
+ | "\[" INTEGER "]"
+ | <Variable+>("[" INTEGER? "]")
+ | <Variable+>("{" "}")
)
;
-TypeUnaryKeyword ::= | TypeUnarySymbol | "mutable" TypeUnaryKeyword;
+TypeUnaryKeyword<Variable> ::= | TypeUnarySymbol<?Variable> | "mutable" TypeUnaryKeyword<?Variable>;
-TypeIntersection ::= (TypeIntersection "&")? TypeUnaryKeyword;
-TypeUnion ::= (TypeUnion "|")? TypeIntersection;
+TypeIntersection <Variable> ::= (TypeIntersection <?Variable> "&")? TypeUnaryKeyword<?Variable>;
+TypeUnion <Variable> ::= (TypeUnion <?Variable> "|")? TypeIntersection<?Variable>;
-Type ::= TypeUnion;
+Type<Variable> ::= TypeUnion<?Variable>;
-DeclarationType ::= "type" IDENTIFIER "=" Type ";";
-DeclarationVariable ::= "let" "unfixed"? IDENTIFIER ":" Type "=" Expression<+Variable> ";";
+DeclarationType ::= "type" IDENTIFIER "=" Type<+Variable> ";";
+DeclarationVariable ::= "let" "unfixed"? IDENTIFIER ":" Type<+Variable> "=" Expression<+Variable> ";";
+SemanticTypeCollectionLiteral =:=
+ | SemanticTypeTuple
+ | SemanticTypeRecord
+ | SemanticTypeDict
+ | SemanticTypeSet
+ | SemanticTypeMap
+ | SemanticTypeList
+;
-SemanticTypeTuple ::= SemanticItemType*;
-SemanticTypeRecord ::= SemanticPropertyType+;
-SemanticTypeDict ::= SemanticType;
-SemanticTypeSet ::= SemanticType;
-SemanticTypeMap ::= SemanticType SemanticType;
-SemanticTypeList[count: RealNumber | None]
+SemanticTypeTuple [isRef: Boolean] ::= SemanticItemType*;
+SemanticTypeRecord [isRef: Boolean] ::= SemanticPropertyType+;
+SemanticTypeDict ::= SemanticType;
+SemanticTypeSet ::= SemanticType;
+SemanticTypeMap ::= SemanticType SemanticType;
+SemanticTypeList[isRef: Boolean][count: RealNumber | None]
::= SemanticType; // Note: a SemanticTypeList with a `count` represents a Tuple Type; otherwise it represents a List Type.
-Decorate(TypeGrouped ::= "(" Type ")") -> SemanticType
- := Decorate(Type);
+Decorate(TypeGrouped<?Variable> ::= "(" Type<?Variable> ")") -> SemanticType
+ := Decorate(Type<?Variable>);
-Decorate(TypeTupleLiteral ::= "[" "]") -> SemanticTypeTuple
- := (SemanticTypeTuple);
-Decorate(TypeTupleLiteral ::= "[" ","? ItemsType "]") -> SemanticTypeTuple
- := (SemanticTypeTuple
- ...Decorate(ItemsType)
+Decorate(TypeTupleLiteral<-Variable> ::= "\[" "]") -> SemanticTypeTuple
+ := (SemanticTypeTuple[isRef=false]);
+Decorate(TypeTupleLiteral<+Variable> ::= "[" "]") -> SemanticTypeTuple
+ := (SemanticTypeTuple[isRef=true]);
+Decorate(TypeTupleLiteral<-Variable> ::= "\[" ","? ItemsType<?Variable> "]") -> SemanticTypeTuple
+ := (SemanticTypeTuple[isRef=false]
+ ...Decorate(ItemsType<?Variable>)
+ );
+Decorate(TypeTupleLiteral<+Variable> ::= "[" ","? ItemsType<?Variable> "]") -> SemanticTypeTuple
+ := (SemanticTypeTuple[isRef=true]
+ ...Decorate(ItemsType<?Variable>)
);
-Decorate(TypeRecordLiteral ::= "[" ","? PropertiesType "]") -> SemanticTypeRecord
- := (SemanticTypeRecord
- ...Decorate(PropertiesType)
+Decorate(TypeRecordLiteral<-Variable> ::= "\[" ","? PropertiesType<?Variable> ","? "]") -> SemanticTypeRecord
+ := (SemanticTypeRecord[isRef=false]
+ ...Decorate(PropertiesType<?Variable>)
+ );
+Decorate(TypeRecordLiteral<+Variable> ::= "[" ","? PropertiesType<?Variable> ","? "]") -> SemanticTypeRecord
+ := (SemanticTypeRecord[isRef=true]
+ ...Decorate(PropertiesType<?Variable>)
);
-Decorate(TypeDictLiteral ::= "[" ":" Type "]") -> SemanticTypeDict
+Decorate(TypeDictLiteral ::= "[" ":" Type<+Variable> "]") -> SemanticTypeDict
:= (SemanticTypeDict
- Decorate(Type)
+ Decorate(Type<+Variable>)
);
-Decorate(TypeMapLiteral ::= "{" Type__0 "->" Type__1 "}") -> SemanticTypeMap
+Decorate(TypeMapLiteral ::= "{" Type__0<+Variable> "->" Type__1<+Variable> "}") -> SemanticTypeMap
:= (SemanticTypeMap
- Decorate(Type__0)
- Decorate(Type__1)
+ Decorate(Type__0<+Variable>)
+ Decorate(Type__1<+Variable>)
);
-Decorate(GenericArguments ::= "<" ","? Type# ","? ">") -> Sequence<SemanticType>
- := ParseList(Type, SemanticType);
+Decorate(GenericArguments ::= "<" ","? Type<+Variable># ","? ">") -> Sequence<SemanticType>
+ := ParseList(Type<+Variable>, SemanticType);
Decorate(TypeUnit ::= KEYWORD_TYPE) -> SemanticTypeConstant
:= (SemanticTypeConstant[value=KeywordType(KEYWORD_TYPE)]);
-Decorate(TypeUnit ::= IDENTIFIER) -> SemanticTypeAlias
+Decorate(TypeUnit<+Variable> ::= IDENTIFIER) -> SemanticTypeAlias
:= (SemanticTypeAlias[id=TokenWorth(IDENTIFIER)]);
-Decorate(TypeUnit ::= PrimitiveLiteral) -> SemanticTypeConstant
+Decorate(TypeUnit<Variable> ::= PrimitiveLiteral) -> SemanticTypeConstant
:= (SemanticTypeConstant[value=ToType(Decorate(PrimitiveLiteral).value)]);
-Decorate(TypeUnit ::= TypeGrouped) -> SemanticType
- := Decorate(TypeGrouped);
-Decorate(TypeUnit ::= TypeTupleLiteral) -> SemanticTypeTuple
- := Decorate(TypeTupleLiteral);
-Decorate(TypeUnit ::= TypeRecordLiteral) -> SemanticTypeRecord
- := Decorate(TypeRecordLiteral);
-Decorate(TypeUnit ::= TypeDictLiteral) -> SemanticTypeDict
+Decorate(TypeUnit<Variable> ::= TypeGrouped<Variable>) -> SemanticType
+ := Decorate(TypeGrouped<Variable>);
+Decorate(TypeUnit<Variable> ::= TypeTupleLiteral<-Variable>) -> SemanticTypeTuple
+ := Decorate(TypeTupleLiteral<-Variable>);
+Decorate(TypeUnit<Variable> ::= TypeRecordLiteral<-Variable>) -> SemanticTypeRecord
+ := Decorate(TypeRecordLiteral<-Variable>);
+Decorate(TypeUnit<+Variable> ::= TypeTupleLiteral<?Variable>) -> SemanticTypeTuple
+ := Decorate(TypeTupleLiteral<?Variable>);
+Decorate(TypeUnit<+Variable> ::= TypeRecordLiteral<?Variable>) -> SemanticTypeRecord
+ := Decorate(TypeRecordLiteral<?Variable>);
+Decorate(TypeUnit<+Variable> ::= TypeDictLiteral) -> SemanticTypeDict
:= Decorate(TypeDictLiteral);
-Decorate(TypeUnit ::= TypeMapLiteral) -> SemanticTypeMap
+Decorate(TypeUnit<+Variable> ::= TypeMapLiteral) -> SemanticTypeMap
:= Decorate(TypeMapLiteral);
-Decorate(TypeUnarySymbol ::= TypeCompound) -> SemanticType
- := Decorate(TypeCompound);
-Decorate(TypeUnarySymbol ::= TypeUnarySymbol "?") -> SemanticTypeOperation
+Decorate(TypeUnarySymbol<Variable> ::= TypeCompound<?Variable>) -> SemanticType
+ := Decorate(TypeCompound<?Variable>);
+Decorate(TypeUnarySymbol<Variable> ::= TypeUnarySymbol<?Variable> "?") -> SemanticTypeOperation
:= (SemanticTypeOperation[operator=ORNULL]
- Decorate(TypeUnarySymbol)
+ Decorate(TypeUnarySymbol<?Variable>)
);
-Decorate(TypeUnarySymbol ::= TypeUnarySymbol "!") -> SemanticTypeOperation
+Decorate(TypeUnarySymbol<Variable> ::= TypeUnarySymbol<?Variable> "!") -> SemanticTypeOperation
:= (SemanticTypeOperation[operator=OREXCP]
- Decorate(TypeUnarySymbol)
+ Decorate(TypeUnarySymbol<?Variable>)
+ );
+Decorate(TypeUnarySymbol<Variable> ::= TypeUnarySymbol<?Variable> "\[" INTEGER "]") -> SemanticTypeList
+ := (SemanticTypeList[isRef=false][count=TokenWorth(INTEGER)]
+ Decorate(TypeUnarySymbol<?Variable>)
);
-Decorate(TypeUnarySymbol ::= TypeUnarySymbol "[" "]") -> SemanticTypeList
- := (SemanticTypeList[count=none]
- Decorate(TypeUnarySymbol)
+Decorate(TypeUnarySymbol<+Variable> ::= TypeUnarySymbol<?Variable> "[" "]") -> SemanticTypeList
+ := (SemanticTypeList[isRef=true][count=none]
+ Decorate(TypeUnarySymbol<?Variable>)
);
-Decorate(TypeUnarySymbol ::= TypeUnarySymbol "[" INTEGER "]") -> SemanticTypeList
- := (SemanticTypeList[count=TokenWorth(INTEGER)]
- Decorate(TypeUnarySymbol)
+Decorate(TypeUnarySymbol<+Variable> ::= TypeUnarySymbol<?Variable> "[" INTEGER "]") -> SemanticTypeList
+ := (SemanticTypeList[isRef=true][count=TokenWorth(INTEGER)]
+ Decorate(TypeUnarySymbol<?Variable>)
);
-Decorate(TypeUnarySymbol ::= TypeUnarySymbol "{" "}") -> SemanticTypeSet
+Decorate(TypeUnarySymbol<+Variable> ::= TypeUnarySymbol<?Variable> "{" "}") -> SemanticTypeSet
:= (SemanticTypeSet
- Decorate(TypeUnarySymbol)
+ Decorate(TypeUnarySymbol<?Variable>)
);
-Decorate(TypeUnaryKeyword ::= TypeUnarySymbol) -> SemanticType
- := Decorate(TypeUnarySymbol);
-Decorate(TypeUnaryKeyword ::= "mutable" TypeUnaryKeyword) -> SemanticTypeOperation
+Decorate(TypeUnaryKeyword<Variable> ::= TypeUnarySymbol<?Variable>) -> SemanticType
+ := Decorate(TypeUnarySymbol<?Variable>);
+Decorate(TypeUnaryKeyword<Variable> ::= "mutable" TypeUnaryKeyword<?Variable>) -> SemanticTypeOperation
:= (SemanticTypeOperation[operator=MUTABLE]
- Decorate(TypeUnaryKeyword)
+ Decorate(TypeUnaryKeyword<?Variable>)
);
-Decorate(Type ::= TypeUnion) -> SemanticType
- := Decorate(TypeUnion);
+Decorate(Type<Variable> ::= TypeUnion<?Variable>) -> SemanticType
+ := Decorate(TypeUnion<?Variable>);
-Decorate(DeclarationType ::= "type" IDENTIFIER "=" Type ";") -> SemanticDeclarationType
+Decorate(DeclarationType ::= "type" IDENTIFIER "=" Type<+Variable> ";") -> SemanticDeclarationType
:= (SemanticDeclarationType
(SemanticTypeAlias[id=TokenWorth(IDENTIFIER)])
- Decorate(Type)
+ Decorate(Type<+Variable>)
);
-Decorate(DeclarationVariable ::= "let" IDENTIFIER ":" Type "=" Expression<+Variable> ";") -> SemanticDeclarationVariable
+Decorate(DeclarationVariable ::= "let" IDENTIFIER ":" Type<+Variable> "=" Expression<+Variable> ";") -> SemanticDeclarationVariable
:= (SemanticDeclarationVariable[unfixed=false]
(SemanticVariable[id=TokenWorth(IDENTIFIER)])
- Decorate(Type)
+ Decorate(Type<+Variable>)
Decorate(Expression<+Variable>)
);
-Decorate(DeclarationVariable ::= "let" "unfixed" IDENTIFIER ":" Type "=" Expression<+Variable> ";") -> SemanticDeclarationVariable
+Decorate(DeclarationVariable ::= "let" "unfixed" IDENTIFIER ":" Type<+Variable> "=" Expression<+Variable> ";") -> SemanticDeclarationVariable
:= (SemanticDeclarationVariable[unfixed=true]
(SemanticVariable[id=TokenWorth(IDENTIFIER)])
- Decorate(Type)
+ Decorate(Type<+Variable>)
Decorate(Expression<+Variable>)
);
Type TypeValueOf(SemanticTypeList listtype) :=
1. *Assert:* `listtype.children.count` is 1.
+ 2. *If* `listtype.isRef` is `false`:
+ 1. *Assert:* `listtype.count` is a RealNumber.
3. *Let* `itemtype` be *UnwrapAffirm:* `TypeValueOf(listtype.children.0)`.
4. *If* `listtype.count` is a RealNumber:
...
5. *Return:* a `List` type whose items’ type is `itemtype`.
;
Type TypeValueOf(SemanticTypeOperation[operator: MUTABLE] oper) :=
1. *Assert:* `oper.children.count` is 1.
2. *Let* `child` be *UnwrapAffirm:* `TypeValueOf(oper.children.0)`.
- 3. *Let* `returned` be a mutable copy of `child`.
- 4. *Return:* `returned`.
+ 3. *If* `child` is a SemanticTypeTuple or a SemanticTypeRecord or a SemanticTypeList:
+ 1. *If* `child.isRef` is `false`:
+ 1. *Throw:* a new TypeError01.
+ 4. *Return:* a mutable copy of `child`.
;
Constant collections are a literal syntax of collections that contain only constants or other constant collections.
Description
Constant collections are constructed via a specific syntax, but they also have certain semantics, as described in another issue. Constant collections may only take the form of Strings, Tuples, and Records; other data structures such as Lists, Hashes, Sets, and Maps may not be constant collections.
Foldable Expressions
Foldable expressions are expressions whose values are either constant or computable by the compiler and thus are reduced to a single constant value at compile time. Foldable expressions are not enforced via a syntax production, as there is no way to determine syntactically whether the compiler can compute the value.
For example, if
x
is known by the compiler to be fixed and immutable at the value[3]
, then the expressionx.0 + 2
is foldable, and the compiler will reduce the output to5
. Sincex
is guaranteed to never be reassigned or modified, the compiler is able to make this optimization. This is called Constant Folding (#34). Ifx
were unfixed or mutable, however, then the expressionx.0 + 2
would not be foldable, even though it’s syntactically identical. Any expression that contains primitive literals, fixed immutable variables, and operators is foldable, and any compound types made of foldable expressions is also foldable. E.g., usingx
above, this includes the Set{2, x.0, x.0 + 2, x}
. String templates that interpolate only foldable expressions (or have no interpolation at all) are also foldable. Some function calls may be foldable, but only if the function can be executed at compile-time.Expressions that contain unfixed or mutable variables, most function/method/constructor calls, function expressions (lambdas), or class expressions are not foldable.
Constant Expressions
Constant expressions, by contrast, are a pure syntactic production. Constant expressions include only primitive literals, string literals, operators, template literals containing only constant expressions, and constant collections (below), which are special-syntaxed tuples and records that contain only constant expressions. Think of them as a more restrictive form of foldable expressions — All constant expressions are foldable, but not all foldable expressions are constant.
Constant expressions do not include variables of any kind (even if they are unfixed), regular tuples/records, other compound data types, function calls, or function/class expressions. Expressions that are not constant are called variable.
Constant Collections
Constant collections are constant expressions that take the form of tuple/record literals whose entries are other constant expressions. They are written with the token
\
preceding the usual[ … ]
. Nesting is allowed, but constant collections must not contain variable collections.Vects are the tuple form of constant expressions. (“Vect” is short for “vector”, but not to be confused with the Vector Counterpoint Specification Type.)
[4]
andx
are variable expressions and are not allowed inside a constant collection. This rule is enforced via syntactic analysis.Structs are the record form of constant expressions. (“Struct” is short for “structure”, but again, not to be confused with the Structure Counterpoint Specification Type.)
If a variable collection meets the criteria of a constant collection, that is, it contains only constant expressions, then converting the variable collection to a constant collection (prepending a
\
token) will reduce compilation time and produce a value object. For example, it is recommended to convert['who', 'what', 'i don’t know']
into\['who', 'what', 'i don’t know']
where possible.The constant collection syntax parses and evaluates much faster in comparison to variable collections where there could be functions to call, members and identifiers to access, and pointers such as
this
andsuper
to dereference.Semantics of Vects and Structs
Vects and Structs generate value objects. Value objects have no identity and are described only by their value. In contrast, reference objects are identified by reference.
As value objects, Vects and Structs are immutable: It’s impossible to change their entries, and it’s impossible to assign them to mutable types. Vects and Structs are very much like strings: even though a string is an array of characters, we cannot mutate it.
However, it is possible to assign a Vect to a variable tuple type, as long as that type is not mutable. Same with Structs.
As with all value objects, equality implies identity. Vects/Structs that have the same entries are considered identical: Comparing them with the
===
operator will returntrue
. Vects/Structs that are equal are the same value.Specification
Lexical Grammar
Syntactic Grammar
Semantics
Decorate
TypeOf
Intrinsics
ValueOf
Identical
Equal