Angle brackets or comparison
A `<` after a name starts a type argument list only if what follows parses as types up to a matching `>` that is itself followed by a token a comparison could not have.
Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite
load<Config>(path) and a < b both start with a name and a <. The parser tells them apart without knowing what
any of the names mean: it tries to read a type argument list, and falls back to "less than" the moment that reading
does not work out.
Example#
fn identity<Value>(value: Value): Value {
value
}
const a = 1
const b = 2
print identity<Int>(5)
print(a < b)Syntax#
name<Type, Type> a type argument list, when the reading below succeeds
name < value "less than", otherwiseRules#
After a name,
<is tried as the start of a type argument list first. The parser reads what follows as one or more types, separated by commas, up to a>.The attempt only succeeds if the token right after the matching
>could not continue a comparison. That token has to be one of(,),],{,},:,,,.,?,==,!=, or the end of the line. Anything else - most importantly another comparison operator - means the<was "less than" all along, and the parser backs out and re-reads the whole thing as an expression.A comparison never loses a valid expression to this reading, because comparisons do not chain:
a < b > cis never a legal comparison in the first place, so the generic reading cannot be mistaken for one that would have parsed.Kompiliert nichtconst a = 1 const b = 2 const c = 3 print a < b > c // error: Comparisons do not chain. Use `&&`: `a < b && b < c`In a type position there is no ambiguity to resolve. After
:,with,where, or inside another type's angle brackets,<always starts a type argument list, because "less than" is not an expression a type position could contain.The decision is made without looking up what a name refers to.
identity<Int>reads as a type argument list becauseIntparses as a type, whether or notidentityturns out to be generic; a wrong reading here is a type error at the call, not a different parse.
What this is not#
Whitespace is not part of the decision. identity<Int>(5) and identity <Int> (5) are the same call; the
choice between a type argument list and "less than" comes only from what follows the <, never from where the
spaces are.
fn identity<Value>(value: Value): Value {
value
}
print identity <Int> (5)A missing closing > is not read as an open comparison, either: it falls back to "less than" the moment the type
reading cannot finish, which is what makes total < Int below an ordinary comparison rather than a stalled
attempt at a type argument list.
const total = 1
const flag = total < Int
print flag
// error: Expected `Int64`, found `() => Int64`Related#
- Naming - why a type parameter is written out (
Value), not abbreviated (T). - Declaring a type - where the generic parameters of a type are declared.
- Syntax cheat sheet - every declaration and expression form on one page.