Mojo expression reference
An expression is any piece of code that produces a value. Expressions are the building blocks of computation: you combine them with operators, pass them as arguments, assign their results to variables, and use them as conditions in control flow.
Identifier expressions
An identifier refers to a named element: a variable, function, type, or module. Using an identifier in an expression gives you the thing it refers to:
score # a variable
Int # a type
range # a functionParenthesized expressions
Parentheses group subexpressions, overriding default precedence:
(a + b) * c # add a and b, multiply by c
(x) # just xParentheses also let expressions span multiple lines without using backslash escapes:
var result = (
first_value
+ second_value
+ third_value
)Tuples
A tuple is a fixed-size, ordered group of values. The comma is what creates a tuple, not the parentheses:
var a = 2, 3 # tuple without parentheses
var b = (2, 3) # same tuple with parentheses
var x, y = b # x is 2, y is 3Use a trailing comma to create a one-element tuple. Without it,
(1) is just the integer 1 in parentheses:
() # empty tuple
(1,) # one-element tuple
(1, 2, 3) # three-element tupleTuples support indexing:
var point = (10, 20)
print(point[0]) # 10Collection displays
The compiler calls these displays. Displays are similar to literals, but unlike literals, displays can contain expressions as well as fixed values.
Lists
A list display creates a list from comma-separated values:
var empty = []
var numbers = [1, 2, 3]
var mixed = [1, "two", 3.0,]Mojo allows trailing commas after all collection elements, including the final one.
Dictionaries
A dict display maps keys to values with : between each
pair:
var empty = {}
var ages = {"Alice": 30, "Bob": 25}Sets
A set display uses braces with values but no colons:
var primes = {2, 3, 5, 7}Brace syntax also serves as an initializer list that creates an instance of an inferred type. Without type context, the compiler can't distinguish a set from an initializer list, so the distinction is resolved at type-check time. Initializer lists can include positional values and keyword arguments:
{x, y} # set or initializer list, without context
{z=4, "foo"} # initializer list with keyword argumentInitializer lists are syntactic sugar for constructor calls.
{1, "hello"} is equivalent to T(1, "hello") when the
type T is known from context. Use them for passing
initialized instances as arguments:
process({1, "hello"}) # type inferred from signatureMember access
The dot operator accesses an attribute or method on a value:
var length = text.count()
var x = point.x
var name = person.name.upper()Chaining is left to right: a.b.c accesses c on the
result of a.b.
Calls
A call expression invokes a function or constructs a value
by appending () to an expression:
print("hello")
var result = compute(a, b)
var p = Point(1.0, 2.0)Positional and keyword arguments
Arguments before any keyword argument are positional. Keyword
arguments use name=value syntax. Positional arguments can't
follow keyword arguments:
def greet(name: String, loud: Bool = False):
pass
greet("Alice")
greet("Alice", loud=True)
greet(name="Bob")Subscripts and slices
Square brackets after an expression look up a value by index(es) or key(s):
var item = collection[0]
var value = mapping["key"]
var cell = matrix[i, j]Slices
Colons inside square brackets create slices. Slices select
a range of elements using start:stop or
start:stop:stride:
var items = [0, 1, 2, 3, 4, 5]
var first_three = items[0:3] # [0, 1, 2] (3 not included)
var from_three = items[3:] # [3, 4, 5]
var every_other = items[::2] # [0, 2, 4]
var reversed = items[::-1] # [5, 4, 3, 2, 1, 0]All three parts are optional. Start defaults to the beginning, stop defaults to the end, and stride defaults to 1. The element at the stop position isn't included in the result.
Ternary conditional
The if-else expression selects between two values based
on a condition:
var label = "even" if x % 2 == 0 else "odd"The condition follows if, and the alternate value follows
else. If the condition is true, the expression evaluates to
the first value. If false, the alternate.
Ternary expressions are right-associative and can be chained:
var size = (
"small" if n < 10
else "large" if n > 100
else "medium"
)This groups as
"small" if n < 10 else ("large" if n > 100 else "medium").
Walrus operator
Regular assignment (=) is a statement. It can't appear
inside a larger expression. The walrus operator := is
the expression form of assignment: it binds a value to a
name and evaluates to that same value, so the result can
be used immediately:
if (n := len(items)) > 10:
print(n)
while (name := input("Prompt: ")) != "quit":
print(name)The walrus operator implicitly declares the variable in
def functions. It has the lowest precedence of any
expression operator, so parentheses are typically needed
when used inside larger expressions.
Compile-time expressions
comptime forces an expression to evaluate at compile time.
Parentheses are required:
def heavy_calculation() -> Int:
var sum = 0
for i in range(1_000_000):
sum += i
return sum
var x = comptime(heavy_calculation()) # O(1) at runtime
print(x) # 499999500000The loop runs once during compilation. At runtime, x is
a constant. If the expression can't be evaluated at compile
time, the compiler reports an error.
Mojo also provides built-in expressions for compile-time type introspection. These look like function calls but they're keywords that operate on types and traits at compile time:
type_of(x) # type of an expression
conforms_to(T, Trait) # test trait conformance
origin_of(x) # origin of a referenceThese three expressions don't work the way most people expect. They're used for reflection, conditional type conformance, and origin sets. They return compiler-internal types and won't print.
Comprehension expressions
A comprehension is a concise way to build a new collection by iterating over existing values and optionally filtering or transforming them. It replaces common loop-and-append patterns with a single, (usually) readable expression.
List comprehensions
List comprehensions create lists:
var squares = [x * x for x in [0, 1, 2, 3, 4] if x % 2 == 0]
# [0, 4, 16]
var positive = [x for x in range(-3, 3) if x > 0]
# [1, 2]Syntax: [expr for pattern in iterable if condition]
- Multiple
forclauses create nested iteration ifclauses filter elements
Set comprehensions
Set comprehensions create sets. Sets don't store duplicates, so you may get fewer elements than iterations:
var fibs = {fib(x) for x in range(6)}
# {1, 2, 3, 5, 8}, 5 elements from 6 iterationsSyntax: {expr for pattern in iterable if condition}
Dictionary comprehensions
Dictionary comprehensions create dictionaries:
var dict_squares = {x: x * x for x in range(3)}
# {0: 0, 1: 1, 2: 4}
var lengths: Dict[String, Int] = {
k: len(k) for k in ["one", "two", "three", "four"]
}
# {one: 3, two: 3, three: 5, four: 4}Syntax:
{key_expr: value_expr for pattern in iterable if condition}
Comprehension clauses
Comprehensions support multiple for and if clauses:
var products = [
(x, y, x * y)
for x in range(3)
for y in range(3)
if (x + y) % 2 == 0
]
# [(0, 0, 0), (0, 2, 0), (1, 1, 1), (2, 0, 0), (2, 2, 4)]Clauses are evaluated left to right:
- Each
forintroduces a new iteration variable - Each
iffilters based on the current values
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!