Mojo has changed how def
function arguments are processed. Previously, by default, arguments to a def
were treated according to the owned
convention, which makes a copy of the value, enabling that value to be mutable in the callee.
This could lead to major performance issues because of the proliferation of unnecessary copies. It also required you to declare non-copyable types as borrowed
explicitly. Now Mojo takes a different approach: def
functions take arguments as borrowed
by default (consistent with fn
functions) but will make a local copy of the value only if the argument is mutated in the body of the function.
This improves consistency, performance, and ease of use.
Implicit variable definitions in a def
function are more flexible: you can now implicitly declare variables as the result of a tuple return, using a,b,c = foo()
. For example:
def return_two(i: Int) -> (Int, Int): return i, i+1a, b = return_two(5)
Implicit variable declarations can also now shadow global immutable symbols (such as module names and built-ins) without getting a compiler error. For example:
slice = foo()
Mojo functions can return an auto-dereferenced refeference to storage with a new ref
keyword in the result type specifier. For example:
@valuestruct Pair: var first: Int var second: Int fn get_first_ref(inout self) -> ref[__lifetime_of(self)] Int: return self.firstfn show_mutation(): var somePair = Pair(5, 6) somePair.get_first_ref() = 1
This approach provides a general way to return an "automatically dereferenced" reference of a given type. Notably, this eliminates the need for __refitem__()
to exist. __refitem__()
has thus been removed and replaced with __getitem__()
that returns a reference.
Mojo added support for infer-only parameters. Infer-only parameters must appear at the beginning of the parameter list and cannot be explicitly specified by the user. They are declared to the left of a //
marker, much like positional-only parameters. This allows programmers to define functions with dependent parameters to be called without the caller specifying all the necessary parameters. For example:
fn parameter_simd[dt: DType, //, value: Scalar[dt]](): print(value)fn call_it(): parameter_simd[Int32(42)]()
In the above example, Int32(42)
is passed directly into value
, the first parameter that isn't infer-only. dt
is inferred from the parameter itself to be DType.int32
.
This also works with structs. For example:
struct ScalarContainer[dt: DType, //, value: Scalar[dt]]: passfn foo(x: ScalarContainer[Int32(0)]): # 'dt' is inferred as `DType.int32` pass
This should make working with dependent parameters more ergonomic. See Infer-only parameters in the Mojo Manual.
Mojo now allows functions overloaded on parameters to be resolved when forming references to, but not calling, those functions. For example, the following now works:
fn overloaded_parameters[value: Int32](): passfn overloaded_parameters[value: Float32](): passfn form_reference(): alias ref = overloaded_parameters[Int32()] # works!
Mojo now supports adding a @deprecated
decorator on structs, functions, traits, aliases, and global variables. The decorator marks the attached declaration as deprecated and causes a warning to be emitted when the deprecated declaration is referenced in user code. The decorator requires a deprecation message, specified as a string literal.
@deprecated("Foo is deprecated, use Bar instead")struct Foo: passfn outdated_api(x: Foo): # warning: Foo is deprecated, use Bar instead pass@deprecated("use another function!")fn bar(): passfn techdebt(): bar() # warning: use another function!
Mojo has introduced @parameter for
, a new feature for compile-time programming. @parameter for
defines a for loop where the sequence and the induction values in the sequence must be parameter values. For example:
fn parameter_for[max: Int](): @parameter for i in range(max) @parameter if i == 10: print("found 10!")
Currently, @parameter for
requires the sequence's __iter__()
method to return a _StridedRangeIterator
, meaning the induction variables must be Int
. The intention is to lift these restrictions in the future.
The is_mutable
parameter of Reference
and AnyLifetime
is now a Bool
, not a low-level __mlir_type.i1
value.
This improves the ergonomics of spelling out a Reference
type explicitly.
Mojo will now link to a Python dynamic library based on the Python on top of your search path: PATH
. This enables you to activate a virtual environment like conda
and have access to Python modules installed in that environment without setting MOJO_PYTHON_LIBRARY
. Previously Mojo would find a libpython
dynamic library on installation and put the path in .modular/modular.cfg
, which could result in version conflicts if you activated a virtual environment of a different Python version.
AnyRegType
has been renamed to AnyTrivialRegType
and Mojo now forbids binding non-trivial register-passable types to AnyTrivialRegType
. This closes a major safety hole in the language. Please use AnyType
for generic code going forward.
The let
keyword has been completely removed from the language. We previously removed let
declarations but still provided an error message to users. Now, it is completely gone from the grammar.
Invoking mojo package my-package -o my-dir
on the command line, where my-package
is a Mojo package source directory, and my-dir
is an existing directory, now outputs a Mojo package to my-dir/my-package.mojopkg
. Previously, this had to be spelled out, as in -o my-dir/my-package.mojopkg
.
The Mojo Language Server now reports a warning when a local variable is unused.
Several mojo
subcommands now support a --diagnostic-format
option that changes the format with which errors, warnings, and other diagnostics are printed. By specifying --diagnostic-format json
on the command line, errors and other diagnostics will be output in a structured JSON Lines format that is easier for machines to parse.
The full list of subcommands that support --diagnostic-format
is as follows: mojo build
, mojo doc
, mojo run
, mojo package
, and mojo test
. Further, the mojo test --json
option has been subsumed into this new option; for the same behavior, run mojo test --diagnostic-format json
.
Note that the format of the JSON output may change; we don't currently guarantee its stability across releases of Mojo.
A new --validate-doc-strings
option has been added to mojo
to emit errors on invalid doc strings instead of warnings.
The --warn-missing-doc-strings
flag for mojo
has been renamed to --diagnose-missing-doc-strings
.
A new decorator, @doc_private
, was added that can be used to hide a declaration from being generated in the output of mojo doc
. It also removes the requirement that the declaration has documentation (for example, when used with --diagnose-missing-doc-strings
).
Debugger users can now set breakpoints on function calls in O0 builds even if the call has been inlined by the compiler.
The Mojo Language Server now supports renaming local variables.
The @unroll
decorator has been deprecated and removed. The decorator was supposed to guarantee that a decorated loop would be unrolled, or else the compiler would error. In practice, this guarantee was eroded over time, as a compiler-based approach cannot be as robust as the Mojo parameter system. In addition, the @unroll
decorator did not make the loop induction variables parameter values, limiting its usefulness. Please see @parameter for
for a replacement!
The method object.print()
has been removed. Since object
now conforms to the Stringable
trait, you can use print(my_object)
instead.
The following functions have been removed from the math module:
clamp()
; use the new SIMD.clamp()
method instead.
round_half_down()
and round_half_up()
; these can be trivially implemented using the ceil()
and floor()
functions.
add()
, sub()
, mul()
, div()
, mod()
, greater()
, greater_equal()
, less()
, less_equal()
, equal()
, not_equal()
, logical_and()
, logical_xor()
, and logical_not()
; Instead, users should rely directly on the corresponding operators (+
, -
, *
, /
, %
, >
, >=
, <
, <=
, ==
, !=
, &
, ^
, and ~
).
identity()
and reciprocal()
; users can implement these trivially.
select()
; removed in favor of using SIMD.select()
directly.
is_even()
and is_odd()
; these can be trivially implemented using bitwise &
with 1
.
roundeven()
; the new SIMD.roundeven()
method now provides the identical functionality.
div_ceil()
; use the new ceildiv()
function.
rotate_left()
and rotate_right()
; the same functionality is available in the builtin SIMD.rotate_{left,right}()
methods for SIMD
types, and the bit.rotate_bits_{left,right})()
methods for Int
.
An overload of math.pow()
taking an integer parameter exponent.
align_down_residual()
; it can be trivially implemented using align_down()
.
all_true()
, any_true()
, and none_true()
; use SIMD.reduce_and()
and SIMD.reduce_or()
directly.
reduce_bit_count()
; use the new SIMD.reduce_bit_count()
directly.
rint()
and nearbyint()
; use round()
or SIMD.roundeven()
as appropriate.
The EvaluationMethod
has been removed from math.polynomial
and Estrin's method is no longer available. This method was limited to degree 10 or less, underutilized, and its performance unclear. In the future, this might be reintroduced with an improved implementation if needed, when better performance benchmarking infrastructure is available. The default behavior of math.polynomial.polynomial_evaluate()
is unchanged (Horner's method).
The math.bit.select()
and math.bit.bit_and()
functions have been removed. The same functionality is available in the builtin SIMD.select
and SIMD.__and__()
methods, respectively.
The math.limit
module has been removed. The same functionality is available as follows:
math.limit.inf()
: use utils.numerics.max_or_inf()
math.limit.neginf()
: use utils.numerics.min_or_neg_inf()
math.limit.max_finite()
: use utils.numerics.max_finite()
math.limit.min_finite()
: use utils.numerics.min_finite()
The tensor.random
module has been removed. The same functionality is now accessible via the Tensor.rand()
and Tensor.randn()
static methods.
The builtin SIMD
struct no longer conforms to Indexer
; users must explicitly cast Scalar
values using int
.