---
title: "AEL type inference and limits"
description: "AEL reference: how AEL resolves types at parse time, compile limits, and the full name-collision disambiguation table."
---

# AEL type inference and limits

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

Reference page: part of the [AEL reference](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference). Covers how AEL resolves types at parse time and the language’s hard limits. See [Applies to](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference#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.

| Mechanism | Rule |
| --- | --- |
| Literals | Pin unknown operands (`'hi'` → `STRING`, `10` → `INT`) |
| Operators | Constrain operand types (`and` → `TRILEAN`; numeric `+` requires matching types) |
| Functions | Parameter and return types per the tables in [Functions and terminals](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/functions), [String, BLOB, and HLL functions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/type-functions), and [Operators](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/operators) |
| Path shape | List 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 compare | Comparing 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:

```plaintext
/* 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

| Limit | Value |
| --- | --- |
| Parser stack depth | 512 |
| Compiled expression size | 1 MiB |
| AEL source text size | 1 MiB |

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

::: caution
These limits are enforced on every request, not just at development time. An AEL string that is generated or concatenated dynamically (for example, built from a variable number of user-supplied filter clauses) can grow past a limit later in an application’s life even though the original template compiled fine. Exceeding either limit is a compile-time parse failure, not a partial evaluation — size-check dynamically built AEL text before sending it, especially for expressions that accumulate clauses over time.
:::

## 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.

| Name | Context A | Context 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) |
| `set` | Map/list `setTo(value)` write terminal | `$.blob.bitSet(…)` — BLOB bit range (prefixed with `bit`) |
| `remove` | Collection data type (CDT) `remove()` terminal | `$.blob.bitRemove(…)` — BLOB bytes (prefixed with `bit`) |
| `count` | Path `count()` — collection size or match count | Standalone `countOneBits(x)` — integer bit population |
| `exists` | Path `exists()` on nested values | Bare bin `$.bin.exists()` |
| `type` | Bare bin `$.bin.type()` → particle-type code | Type constants `INT`, `STRING`, and so on in comparisons |
| `toInt` / `toFloat` | Path read terminal — numeric cast on `INT`/`FLOAT` | String method — parse numeric text on `STRING` |
| `~` | Relative binding in selectors `{0:~key}` | Unary bitwise NOT on integers |
| `*` | Path wildcard segment | Literal wildcard in collections |
| `@` | Loop variable in filters | Map key dimension `{@key}` in selectors |

## Next steps

-   [AEL reference overview](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference) — lexical rules, literals, and types
-   [Operators](https://aerospike.com/docs/develop/client/sdk/concepts/ael/reference/operators) — comparison, logical, and arithmetic operators
-   [Author AEL expressions](https://aerospike.com/docs/develop/client/sdk/concepts/ael/authoring-ael-expressions) — filters, read projection, and write expressions on SDK commands