Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Types and Values

Values

  • 42 (a number)
  • "Hello, world!" (a string)
  • b"abc" (a bytes literal)
  • :value (a symbol)
  • [1, 2, 3], array(1, 2, 3) (an array)
  • {"a": 1, "b": 2, "c": 3}, dict(["a", 1], ["b", 2], ["c", 3]) (a dictionary)
  • true, false (a boolean)
  • None

Types

TypeDescriptionExamples
NumberRepresents numeric values.1, 3.14, -42
StringRepresents sequences of characters, including Unicode code points and escape sequences in the form of \{0x000}."hello", "123", "😊", "\u{1F600}"
BytesRepresents a raw byte sequence. Written with a b prefix. Only ASCII characters are allowed unescaped.b"abc", b"\xf0\x9f\x99\x82", b""
SymbolRepresents immutable, interned identifiers prefixed with :. Used for constant values and keys.:value, :success, :error, :ok
BooleanRepresents truth values.true, false
ArrayRepresents ordered collections of values.[1, 2, 3], array(1, 2, 3)
DictRepresents key-value mappings (dictionaries).{"a": 1, "b": 2}, dict(["a", 1], ["b", 2])
FunctionRepresents executable code.def foo(): 42; let name = def foo(): 42;

Number Formatting

Numbers are 64-bit floating point values. They print as the shortest text that parses back to the same value. Magnitudes of 1e21 and above, and non-zero magnitudes below 1e-6, use exponent notation; everything else prints in plain decimal form.

[1e20, 1e21, 0.000001, 1e-7, 1.5e300]
# => [100000000000000000000, 1e21, 0.000001, 1e-7, 1.5e300]

Non-finite Numbers

infinite(), -infinite() and nan() follow jq when written as structured output (JSON, JSONL, YAML, TOML, TOON, XML, gron, -F shell): infinities become 1.7976931348623157e308 / -1.7976931348623157e308 and nan() becomes null. -F csv and -F text write inf, -inf and NaN. YAML .inf, -.inf and .nan are read as the matching numbers.

Byte String Literals

Byte string literals use the b"..." syntax and represent raw sequences of bytes (u8 values).

b"hello"              # 5-byte sequence [104, 101, 108, 108, 111]
b"\xf0\x9f\x99\x82"   # 4-byte emoji encoded as raw bytes
b""                   # empty byte sequence

Allowed characters

Only ASCII characters (code points 0–127) may appear unescaped inside a byte literal. Non-ASCII characters such as é or 😊 must be written using \xNN hex escapes:

# Correct: use \xNN for non-ASCII bytes, here the UTF-8 encoding of 'é' (0xc3, 0xa9)
b"\xc3\xa9" | len
# => 2

# Wrong: non-ASCII characters are not accepted in b"..."
# b"é"          ← syntax error; use \xNN escapes instead

Supported escape sequences

EscapeByte value
\\0x5c (backslash)
\"0x22 (double quote)
\n0x0a (newline)
\r0x0d (carriage return)
\t0x09 (tab)
\00x00 (null)
\xNNArbitrary byte (two hex digits)

Common operations

# Byte length, not character count
b"abc" | len
# => 3
b"abc" | type
# => "bytes"
b"abc" == b"abc"
# => true
b"abc" | is_empty
# => false
b"" | is_empty
# => true

Accessing Values

Array Index Access

Arrays can be accessed using square bracket notation with zero-based indexing:

let arr = [1, 2, 3, 4, 5] |
arr[0]
# => 1

# Third element
let arr = [1, 2, 3, 4, 5] |
arr[2]
# => 3

# Out of range
let arr = [1, 2, 3, 4, 5] |
arr[6] | is_none
# => true

# The index must be an integer
let arr = [1, 2, 3, 4, 5] |
arr[1.5] | is_none
# => true

Indexing a value that cannot be indexed, such as 1[0], is a runtime error. None[0] returns None.

You can also use the get function explicitly:

let arr = [1, 2, 3, 4, 5] |
get(arr, 0)    # Same as arr[0]
# => 1

let arr = [1, 2, 3, 4, 5] |
arr | get(2)   # Same as arr[2]
# => 3

arr[i] and arr[a:b] always use the builtin get and slice. Defining your own get or slice changes only the explicit calls get(arr, i) and slice(arr, a, b).

def get(a, i): "custom" end |
let arr = [1, 2, 3] |
[arr[0], get(arr, 0)]
# => [1, "custom"]

Index Access with .[...]

On arrays, strings and dicts, .[expr] reads the same as self[expr]: negative indices count from the end, and an out-of-range or non-integer index returns None.

[10, 20, 30] | .[-1]
# => 30
"abc" | .[1]
# => "b"
{"a": 1} | .["a"]
# => 1
[10, 20, 30] | .[1.5] | is_none
# => true

On Markdown values, .[n] selects the list item with index n instead (see Selectors).

Array Slice Access

Arrays support slice notation to extract subarrays using the arr[start:end] syntax:

# Elements from index 1 to 3
let arr = [1, 2, 3, 4, 5] |
arr[1:4]
# => [2, 3, 4]

# First three elements
let arr = [1, 2, 3, 4, 5] |
arr[0:3]
# => [1, 2, 3]

# Elements from index 2 to the end
let arr = [1, 2, 3, 4, 5] |
arr[2:5]
# => [3, 4, 5]

Slice indices work as follows:

  • start: The starting index (inclusive)
  • end: The ending index (exclusive)
  • Both indices are zero-based
  • If start or end is out of bounds, it will be clamped to valid range
let arr = [1, 2, 3, 4, 5] |
arr[0:2]
# => [1, 2]

# The end index is clamped to the array length
let arr = [1, 2, 3, 4, 5] |
arr[3:10]
# => [4, 5]

# The slice is empty when start equals end
let arr = [1, 2, 3, 4, 5] |
arr[2:2] | is_empty
# => true

Dictionary Key Access

Dictionaries can be accessed using square bracket notation with keys:

let d = {"name": "Alice", "age": 30, "city": "Tokyo"} |
d["name"]
# => "Alice"

let d = {"name": "Alice", "age": 30, "city": "Tokyo"} |
d["age"]
# => 30

You can also use the get function explicitly:

let d = {"name": "Alice", "age": 30, "city": "Tokyo"} |
get(d, "name")   # Same as d["name"]
# => "Alice"

let d = {"name": "Alice", "age": 30, "city": "Tokyo"} |
d | get("age")   # Same as d["age"]
# => 30

A dictionary literal can be indexed directly:

{"a": 1, "b": {"c": 2}}["a"]
# => 1
{"a": 1, "b": {"c": 2}}["b"]["c"]
# => 2

String Index Access

Strings can be indexed by Unicode scalar value. Negative indices count from the end. An out-of-range or non-integer index returns None:

"abc"[0]
# => "a"
"abc"[-1]
# => "c"
"abc"[3] | is_none
# => true
"abc"[1:3]
# => "bc"

Combining characters and emoji sequences span several scalar values. Use graphemes to split by user-perceived character.

Spread Operator

The ... spread operator expands an array or dict inline inside an array or dict literal.

let a = [1, 2, 3]
| let b = [4, 5, 6]
| [...a, ...b]
# => [1, 2, 3, 4, 5, 6]

let a = [1, 2, 3]
| [0, ...a, 99]
# => [0, 1, 2, 3, 99]
let base = {x: 1, y: 2}
| {...base, y: 99, z: 3}
# => {"x": 1, "y": 99, "z": 3}

When the same key appears more than once, later keys override earlier ones, including keys coming from a spread. Spreading None contributes nothing; spreading any other non-array (in [...]) or non-dict (in {...}) value is a type error.

Dynamic Access

Both arrays and dictionaries support dynamic access using variables:

let arr = [10, 20, 30]
| let index = 1
| arr[index]
# => 20

let d = {"x": 100, "y": 200}
| let key = "x"
| d[key]
# => 100

Tagged Records

A dict that holds true, false or a string in a field keeps it as a tag. The type checker (mq check) uses a test of the tag to read only the variants it allows:

def div(a, b):
  if (b == 0): {ok: false, error: "zero"} else: {ok: true, value: a / b}
end
| let r = div(4, 2)
| if (r["ok"]): r["value"] + 1 else: len(r["error"])

r["ok"], !r["ok"], r["kind"] == "a" and != narrow the record, also when joined with &&, || or not. A variant without the tag reads it as none, so it is left out of a branch that needs the tag to equal a literal. Assigning to the tag (r["ok"] = false) turns the narrowing off for later reads.

A call is checked against the fields the function reads: def val(r): r["value"] - 1; called as val({value: "s"}) is an error at the call.

Environment Variables

A module handling environment-specific functionality.

  • __FILE__: Contains the path to the file currently being processed.
  • __FILE_NAME__: Contains the name of the file currently being processed (without the path).
  • __FILE_STEM__: Contains the stem of the file currently being processed (filename without extension).