Skip to content

AEL type inference and limits

For the complete documentation index see: llms.txt

All documentation pages available in markdown.

Reference page: part of the AEL reference. Covers how AEL resolves types at parse time and the language’s hard limits. See Applies to on the overview page for SDK and Database version requirements.

Type inference summary

Every bin, variable, and sub-expression must resolve to a concrete type at parse time. No implicit default types are applied.

MechanismRule
LiteralsPin unknown operands ('hi' → STRING, 10 → INT)
OperatorsConstrain operand types (and → TRILEAN; numeric + requires matching types)
FunctionsParameter and return types per the tables in Functions and terminals, String, BLOB, and HLL functions, and Operators
Path shapeList selectors → LIST bin; map keys → MAP bin; a wildcard segment pins nothing — when * is the first segment after a bin name, pin :LIST or :MAP on the bin
Explicit suffix$.bin:INT, @:FLOAT, $.l.[0]:INT
Cross-bin compareComparing two bins without a literal requires :TYPE on at least one side

Conflicting types for the same bin or variable, unresolved unknown types, and wrong types for operators are parse errors. toInt() / toFloat() on strings parse numeric text. toInt() / toFloat() on paths cast between numeric representations. toString() formats an INT, FLOAT, BOOL, STRING, or BLOB value as a STRING.

toInt() and toFloat() can be invoked on either numeric or string types. The method name alone does not disambiguate: $.amount.toFloat() is a parse error unless $.amount was pinned earlier in the expression. Use $.amount:INT.toFloat(), $.amount:FLOAT.toInt(), or $.amount:STRING.toFloat() as appropriate.

One example per mechanism:

/* Literals: the literal 10 pins count as INT */
$.count > 10
/* Operators: 'and' requires TRILEAN operands, so both sides must resolve to TRILEAN */
$.active and $.verified
/* Functions: strlen() only accepts a STRING receiver, so name must be STRING */
$.name.strlen() > 5
/* Path shape: [0] is a list selector, so tags must be a LIST bin */
$.tags.[0]
/* Explicit suffix: pins score as FLOAT regardless of surrounding context */
$.score:FLOAT > 3
/* Cross-bin compare: comparing two bins with no literal requires :TYPE on at least one side */
$.a == $.b /* parse error: neither side has a resolvable type */
$.a == $.b:INT /* OK: :INT on b resolves both a and b to INT */

Compile limits

LimitValue
Parser stack depth512
Compiled expression size1 MiB
AEL source text size1 MiB

Parser stack depth applies to nesting and to long left-associative chains (1+1+…, a and b and …).

Name collisions and disambiguation

Several method and token names are reused across contexts. Receiver type and argument arity distinguish colliding names — AEL resolves them at parse time from the receiver type already established at that point in the expression.

NameContext AContext B
insert$.l.[i].insert(value) — list element$.str.splice(offset:, value:) — string chars (named splice, not insert, to avoid this collision)
append$.l.append(value) — list element— (string concatenation uses +, not a string method)
setMap/list setTo(value) write terminal$.blob.bitSet(…) — BLOB bit range (prefixed with bit)
removeCollection data type (CDT) remove() terminal$.blob.bitRemove(…) — BLOB bytes (prefixed with bit)
countPath count() — collection size or match countStandalone countOneBits(x) — integer bit population
existsPath exists() on nested valuesBare bin $.bin.exists()
typeBare bin $.bin.type() → particle-type codeType constants INT, STRING, and so on in comparisons
toInt / toFloatPath read terminal — numeric cast on INT/FLOATString method — parse numeric text on STRING
~Relative binding in selectors {0:~key}Unary bitwise NOT on integers
*Path wildcard segmentLiteral wildcard in collections
@Loop variable in filtersMap key dimension {@key} in selectors

Next steps