<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Packt Deep Engineering: Building with Mojo]]></title><description><![CDATA[A multi-part hands-on series exploring Mojo, a new programming language developed by the AI company Modular.
© 2025 Ivo Balbaert. All rights reserved.]]></description><link>https://deepengineering.net/s/mojo</link><image><url>https://substackcdn.com/image/fetch/$s_!H5BJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png</url><title>Packt Deep Engineering: Building with Mojo</title><link>https://deepengineering.net/s/mojo</link></image><generator>Substack</generator><lastBuildDate>Sun, 28 Jun 2026 17:19:28 GMT</lastBuildDate><atom:link href="https://deepengineering.net/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Packt]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[deepengineering@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[deepengineering@substack.com]]></itunes:email><itunes:name><![CDATA[Packt]]></itunes:name></itunes:owner><itunes:author><![CDATA[Packt]]></itunes:author><googleplay:owner><![CDATA[deepengineering@substack.com]]></googleplay:owner><googleplay:email><![CDATA[deepengineering@substack.com]]></googleplay:email><googleplay:author><![CDATA[Packt]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Building with Mojo (Part 5): Modules and Packages]]></title><description><![CDATA[Organizing code, building packages, and managing dependencies with Pixi]]></description><link>https://deepengineering.net/p/building-with-mojo-part-5-modules</link><guid isPermaLink="false">https://deepengineering.net/p/building-with-mojo-part-5-modules</guid><dc:creator><![CDATA[Ivo Balbaert]]></dc:creator><pubDate>Thu, 05 Feb 2026 05:43:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b532ad4e-978e-4a0a-902e-208cd388148d_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This article is Part 5 of our ongoing series on the Mojo programming language. <strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language">Part 1</a></strong> introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;babc10be-06a6-41f5-b1a7-5ec86aa0f607&quot;,&quot;caption&quot;:&quot;This article, kicks off a multi-part series exploring Mojo, a new programming language developed by the AI company Modular. Mojo is designed for AI-scale performance without abandoning Pythonic ergonomics. In Part 1, we&#8217;ll cover the following topics:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 1): A Language Born for AI and Systems&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-07-16T10:22:41.479Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e127b69e-ec68-43db-88a6-294f5ee6343d_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:168455891,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:6,&quot;comment_count&quot;:1,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd">Part 2</a></strong> covered </em>Mojo&#8217;s SIMD-first model in practice.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;ff42090e-2319-4558-9a9d-e6a010b2d2dd&quot;,&quot;caption&quot;:&quot;This article is Part 2 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 2): Using SIMD in Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-09-11T08:28:42.258Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79fa439e-7d92-49ac-b0a2-2be7b06591cf_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:173251283,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-3-python">Part 3</a></strong> covered converting a Python program to Mojo, and exploring how they interact.</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;9c8614e5-4df0-4d41-bffb-798faba22a7f&quot;,&quot;caption&quot;:&quot;This article is Part 3 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 3): Python and Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-10-09T07:48:01.963Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f602cbb-615d-4272-91c3-2e88d5092315_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-3-python&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:175610468,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:4,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-4-compile">Part 4</a></strong> covered compile-time metaprogramming in Mojo</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;745423e7-020f-47f2-8eb1-835b296e72ec&quot;,&quot;caption&quot;:&quot;This article is Part 4 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 4): Compile-Time Metaprogramming in Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-11-12T08:40:30.383Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0a36e862-d55c-4c0c-b4b3-f1621b2cdc2b_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-4-compile&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:178671265,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:3,&quot;comment_count&quot;:2,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></blockquote><p><strong>This article covers</strong></p><ul><li><p>A review of Mojo&#8217;s standard library.</p></li><li><p>Creating and importing modules.</p></li><li><p>Importing a package from source or compiled.</p></li><li><p>Importing external Mojo packages with Pixi.</p></li></ul><p>Our code examples until now were intentionally small, so we almost never needed more than one <code>.mojo</code> file for the source code. When a software project grows bigger, however, it is natural to group related code together in one or more files&#8212;for example: helper functions, or data-related structures and methods, and so on.</p><p>When this project is meant to execute on its own, it is common to have a <code>main.mojo</code> file as an entry point to the app and as a kind of central hub to oversee the execution. But you can also have code that is only meant to be reused by other projects. These projects typically include APIs (structs with their methods and helper functions) to be imported and used in other Mojo programs, as a library.</p><p>This modular design not only helps to structure a project and organize its code, it also promotes code reuse. A similar concept exists in almost all programming languages, with slightly different rules and functionality, and diverging names such as package (Java, Go), assembly (C#, .NET), library, or module. This last name is also adopted in Mojo and Modular software. Mojo also uses the term package as a group of related modules.</p><p>Moreover, we want our application to be able to import functionality from other modules, namely from the stdlib, other local Mojo modules, or external Mojo modules written by other people or companies.</p><p>We&#8217;ve already seen how to import and use local Python modules or external Python packages like numpy in one of the previous articles.</p><p>In this article, we&#8217;ll discuss how to make and use modules to structure code. This system is quite simple and works a lot like Python. We&#8217;ll give an overview of Mojo&#8217;s stdlib and see how to import local code and external modules. We&#8217;ll also see how Mojo&#8217;s packaging system works, compiling module code into binary packages, which can then be imported into our projects.</p><p>Finally, we discuss the role of Pixi as Mojo&#8217;s package manager.<br>But we&#8217;ll start with what we know already: how to import functionality from the stdlib.</p><p>To follow along, use Pixi to create a project called <code>modules_and_packages</code> and <code>cd</code> into it. All code examples used in this article can be found in the code repo <strong><a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/tree/main/10-%20Modules%20and%20Packages">here</a></strong>.</p><div><hr></div><h1>Using Mojo&#8217;s standard library</h1><p>Importing functionality from the standard library (stdlib) is so common that we&#8217;ve already done it tens of times. For example:</p><pre><code><code>from collections import List
from python import Python, PythonObject
from random import seed, random_ui64
from sys import argv, exit
from testing import assert_equal</code></code></pre><p>Let&#8217;s now get an overview of the stdlib.</p><p>The standard library is batteries included: it tries to provide everything you need as general functionality in Mojo programming. At the time of writing, it contains some 28 packages (and many more submodules), ranging from <code>algorithm</code> to <code>utils</code>. It implements basic data types like <code>Float32</code> and SIMD, collection types like <code>List</code> and <code>Dict</code>, reusable functions and algorithms, and modules for GPU programming. Their number and contents still grow quite often, but a good starting point is the overview you can find <strong><a href="https://docs.modular.com/mojo/lib/#standard-library">here</a></strong>.</p><p>The Mojo stdlib is organized in packages, each package grouping one or more related modules, for example:</p><ul><li><p>The <code>algorithm</code> package groups the <code>functional</code>, <code>memory</code>, and <code>reduction</code> modules.</p></li><li><p>The <code>memory</code> package contains as important submodules <code>memory</code>, <code>span</code>, <code>arc</code>, <code>owner_pointer</code>, <code>pointer</code>, and <code>unsafe_pointer</code>.</p></li></ul><p>Modular has open-sourced the code of the complete Mojo standard library. Here are the current packages and what they contain or implement (you can look at their source code <strong><a href="https://github.com/modular/modular">here</a></strong>):</p><ul><li><p><code>algorithm</code>: higher-order functions and SIMD reduction.</p></li><li><p><code>base64</code>: functions for base64 encoding strings.</p></li><li><p><code>benchmark</code>: used for runtime benchmarking.</p></li><li><p><code>bit</code>: functions for bit manipulations.</p></li><li><p><code>buffer</code>: utilities for buffers and <code>DimList</code>.</p></li><li><p><code>builtin</code>: implements core types, traits, and functions, such as <code>Int</code>, <code>Bool</code>, <code>abs</code>, <code>min</code>, <code>max</code>, and many others. It is a comprehensive package of core language features. Not everything in <code>builtin</code> is automatically available in every Mojo program. You may need to explicitly import what you use.</p></li><li><p><code>collections</code>: the <code>string</code> package, and modules for <code>List</code>, <code>Dict</code>, <code>Set</code>, and so on.</p></li><li><p><code>compile</code>: utilities for compiling and inspecting Mojo code at runtime.</p></li><li><p><code>complex</code>: types and functions for working with complex numbers.</p></li><li><p><code>gpu</code>: low-level programming constructs for working with GPUs.</p></li><li><p><code>hashlib</code>: various hashing algorithms.</p></li><li><p><code>json</code>: a high-performance JSON parser.</p></li><li><p><code>logger</code>: logging functionality.</p></li><li><p><code>math</code>: mathematical constants and utilities.</p></li><li><p><code>memory</code>: memory utilities, pointer types, and the <code>Span</code> type.</p></li><li><p><code>os</code>: operating-system dependent utilities.</p></li><li><p><code>pathlib</code>: contains <code>Path</code> and related functions.</p></li><li><p><code>pwd</code>: access to user and group information from the password database.</p></li><li><p><code>python</code>: using Python within Mojo.</p></li><li><p><code>random</code>: functions for generating random numbers.</p></li><li><p><code>runtime</code>: the runtime package.</p></li><li><p><code>stat</code>: aliases and functions for modes like devices, files, sockets, and symlinks.</p></li><li><p><code>subprocess</code>: for running a command.</p></li><li><p><code>sys</code>: interactions with execution and system environment, foreign function interface (FFI), host target info, and intrinsics.</p></li><li><p><code>tempfile</code>: temporary file functionality.</p></li><li><p><code>testing</code>: testing functionality.</p></li><li><p><code>time</code>: basic utilities for working with time.</p></li><li><p><code>utils</code>: numerics functionality, <code>IndexList</code>, <code>StaticTuple</code>, <code>Variant</code>, <code>Writer</code>, <code>Writable</code>.</p></li></ul><p>If you look up the <code>memory</code> package, for example, you&#8217;ll see that a package is just a collection of Mojo source files (modules) in a directory that includes an <code>__init__.mojo</code> file, which can be empty.</p><p>By organizing modules together in a directory, you can then import all the modules together or individually. The directory name works as the package name when importing the package.</p><p>The prelude is a special module that enumerates a set of entities (types, aliases, functions, from <code>builtin</code> and elsewhere) that are automatically available in every Mojo program. You can find them <strong><a href="https://github.com/modular/modular/blob/main/mojo/stdlib/std/prelude/__init__.mojo">here</a></strong>.</p><p>Now we&#8217;ll analyze the import syntax, which not only works for the stdlib, but also for local and external Mojo modules.</p><div><hr></div><h1>The import syntax</h1><p>You probably noticed that module and package names are written in all lowercase, like in Python. To import functionality (data structures or functions), for example from the <code>memory</code> package, use the following code (see <code>import_syntax.mojo</code>):</p><pre><code><code>from memory import memset, memcpy, UnsafePointer</code></code></pre><p>This enables us to use the functions <code>memset</code> and <code>memcpy</code>, or the struct <code>UnsafePointer</code> directly, without having to qualify it with <code>memory</code>:</p><pre><code><code>var lst = List[Int](1, 2, 3)
_ = UnsafePointer(to=lst)</code></code></pre><p>If you look a bit closer, you might notice something strange. The <code>memset</code> and <code>memcpy</code> functions are defined in a <code>memory</code> submodule of the <code>memory</code> package. Shouldn&#8217;t we import them as:</p><pre><code><code>from memory.memory import memset, memcpy</code></code></pre><p>This works also, but the shorter version <code>from memory import memset, memcpy</code> works because the <code>__init__.mojo</code> file that is also read in when importing <code>memory</code> contains the line:</p><pre><code><code>from .memory import memcmp, memcpy, memset, memset_zero, stack_allocation</code></code></pre><p>(<code>.memory</code> refers to the submodule <code>memory</code> in the current folder.)</p><p>The <code>__init__.mojo</code> file in a package re-exports, as it were, the contents of the submodules to the package level. <em>(Now explain why we don&#8217;t need to write </em><code>from memory.unsafe_pointer import UnsafePointer</code><em>.)</em></p><p>If you want explicitly to import from a submodule, write:</p><pre><code><code>from max.engine import InferenceSession</code></code></pre><p>To avoid name-clashes, or simply to shorten the name, you can use <code>as</code> to define an alias for the thing you are importing, for example:</p><pre><code><code>from memory import UnsafePointer as UPt</code></code></pre><p>You can then use it in code as:</p><pre><code><code>_ = UPt(to=lst)</code></code></pre><p>The general syntax is:</p><ul><li><p><code>from package1 import function1, Struct1</code></p></li><li><p>or: <code>from package1.submodule1 import function2, Struct2</code></p></li></ul><p>To import all definitions, it is possible to use:</p><ul><li><p><code>from module1 import *</code></p></li><li><p>or simply: <code>import module1</code></p></li></ul><p>But doing this is considered a bad practice: only import what you really need to use less resources. That way you also know to which module a certain function or struct belongs.</p><p>For the same reason, when using the syntax <code>import module1</code>, it is idiomatic to qualify the functions or structs with the module name, like this:</p><pre><code><code>import memory
_ = memory.UnsafePointer(to=lst)</code></code></pre><p>Or use an alias like this:</p><pre><code><code>import memory as mem
_ = mem.UnsafePointer(to=lst)</code></code></pre><p>You cannot import the same module more than once. If you do, you get this kind of error:</p><pre><code><code>invalid redefinition of 'memset'</code></code></pre><p>If you have many things to import, enclose them within <code>()</code>, like this:</p><pre><code><code>from sys.info import (
    alignof,
    sizeof,
    bitwidthof,
    simdwidthof,
    simdbitwidth,
    simdbytewidth,
)</code></code></pre><p><code>from</code>/<code>import</code> statements can be written everywhere in code, but code clarity can be enhanced by grouping them at the start of a code file. Writing them in alphabetical order is also advisable when there are many import lines.</p><div><hr></div><h1>Making and importing a Mojo module</h1><p>A Mojo module is just a single Mojo source file that includes code (like an API), suitable for use by other files that import it. The source file name is also the module&#8217;s name. It doesn&#8217;t need a <code>main()</code> function, because it is not meant to be started on its own: the program that imports the module will usually contain a <code>main()</code> function to start the application.</p><p>The module gets its name from the source file name, without extension. For example: a file <code>compute.mojo</code> contains the code for module <code>compute</code>. Let&#8217;s reuse a previous example, rewriting the Python module as a Mojo module <code>compute</code>:</p><pre><code><code>fn mul(n: Int, m: Int) -&gt; Int:
    return n * m

fn pow(n: Int, m: Int) -&gt; Int:
    return n ** m</code></code></pre><p>Now we can import this module (or part of its functionality) in a source file (see <code>main.mojo</code>) in the same folder which can execute:</p><pre><code><code>from compute import mul, pow     #A

fn main():
    var i = 42
    var j = 7
    var res1 = mul(i, j)         #B
    var res2 = pow(i, j)         #B
    print(res1, res2)  # =&gt; 294 230539333248

#A Import functions mul and pow from compute
#B Call functions mul and pow from main.mojo</code></code></pre><p>Importing the <code>mul</code> and <code>pow</code> functions from module <code>compute</code> and executing them in a program could not be easier. The folder structure is:</p><pre><code><code>/project_folder
    main.mojo       #A
    compute.mojo    #B

#A The main program
#B The local compute module</code></code></pre><div><hr></div><h1>Common file structure for a project</h1><p>From <strong>Appendix A</strong> (found at the end of this article) we can learn what the most basic structure of a Mojo project is after creating it and installing the Modular environment:</p><pre><code><code>/project_folder
    .pixi/          #A
    .gitattributes  #B
    .gitignore      #B
    pixi.lock       #C
    pixi.toml       #D

#A Folder with the virtual environment
#B Files needed for a GitHub project
#C Contains versions for all dependencies (auto-generated)
#D The project configuration file</code></code></pre><p>The directory <code>project_folder</code> is also called the project root folder. <code>pixi.toml</code> is the project configuration file, or manifest. It is generated by the pixi tool but can be edited afterwards.</p><p>In Figure 10.1 you see an example project structure based on official documentation and community best practices.</p><p><strong>Key points:</strong></p><ul><li><p>A Mojo project is by default a local repository, ready to be published in a GitHub repo online.</p></li><li><p>The <code>pixi.toml</code> and <code>pixi.lock</code> files are created and managed by the Pixi CLI and define your project&#8217;s dependencies and environment.</p></li><li><p>The <code>.pixi</code> directory contains the virtual environment for the project.</p></li><li><p>Each Mojo package is a directory with an <code>__init__.mojo</code> file to signal it&#8217;s a package. Modules (e.g., <code>module1.mojo</code>) live inside these package directories.</p></li><li><p>You can organize Python code in a separate directory (e.g., <code>py/</code>) if your project uses both Mojo and Python.</p></li><li><p>A <code>tests</code> directory is commonly used for test files, and sometimes a link to the code directory is used to simplify imports during testing.</p></li></ul><p>This structure is flexible and can be adapted to your project&#8217;s needs, but the presence of <code>pixi.toml</code>, <code>.pixi</code>, and <code>init.mojo</code> files in package directories is essential for proper project- and package management by Mojo and Pixi.</p><p>The project&#8217;s app starts running with <code>main.mojo</code>, which imports code from <code>package1</code> and uses it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Mn5m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mn5m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 424w, https://substackcdn.com/image/fetch/$s_!Mn5m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 848w, https://substackcdn.com/image/fetch/$s_!Mn5m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 1272w, https://substackcdn.com/image/fetch/$s_!Mn5m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mn5m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png" width="756" height="374" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/abe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:374,&quot;width&quot;:756,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35121,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/186845507?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Mn5m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 424w, https://substackcdn.com/image/fetch/$s_!Mn5m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 848w, https://substackcdn.com/image/fetch/$s_!Mn5m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 1272w, https://substackcdn.com/image/fetch/$s_!Mn5m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fabe38f2c-05e5-474a-94f3-3b51f0aec7c2_756x374.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Fig. 1 &#8211; Typical project folder structure. In our example, </em><code>package1</code><em> would be </em><code>compute</code><em>.</em><br>In general, when there are more packages than <code>package1</code>, they can exist parallel to each other, or all be subfolders of one folder <code>src</code>.</p><div><hr></div><h1>Importing a package from source files</h1><p>Let&#8217;s now reorganize our project to this structure, calling it <code>compute_project</code>.<br>In our folder <code>modules_and_packages</code> do:</p><pre><code><code>$ pixi init compute_project -c "https://conda.modular.com/max-nightly" -c "https://repo.prefix.dev/modular-community" -c "conda-forge"
&#10004; Created /$HOME/mojo_book/modules_and_packages/compute_project/pixi.toml
$ cd compute_project
$ pixi shell</code></code></pre><p>Now the current Modular virtual environment is installed, and you can check the Mojo version with:</p><pre><code><code>$ mojo -v
# =&gt; mojo 25.4.0.dev2025052116 (fb591a52)</code></code></pre><p>Copy the file <code>main.mojo</code> to folder <code>compute_project</code>. In it, create a folder <code>compute</code>, and move <code>compute.mojo</code> in it. But if we now try to run the program, we get an error:</p><pre><code><code>$HOME/mojo_book/modules_and_packages/compute_project/main.mojo:1:6:
error: unable to locate module 'compute'
from compute import mul, pow
     ^</code></code></pre><p>The reason is that folder <code>compute</code> is not recognized as a package. To remedy that, it needs a (possibly empty) <code>__init__.mojo</code> file, so we create this. The folder structure of <code>compute_project</code> has now become:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QYOh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QYOh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 424w, https://substackcdn.com/image/fetch/$s_!QYOh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 848w, https://substackcdn.com/image/fetch/$s_!QYOh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 1272w, https://substackcdn.com/image/fetch/$s_!QYOh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QYOh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png" width="368" height="194" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:194,&quot;width&quot;:368,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3633,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/186845507?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QYOh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 424w, https://substackcdn.com/image/fetch/$s_!QYOh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 848w, https://substackcdn.com/image/fetch/$s_!QYOh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 1272w, https://substackcdn.com/image/fetch/$s_!QYOh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc729bf0-61b3-4f56-bc5d-8c4a09f36783_368x194.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><em>Fig. 10.2 &#8211; Folder structure of compute_project</em></p><p>Now the package <code>compute</code> is recognized, but we still get an error when trying to run:</p><pre><code><code>error: package 'compute' does not contain 'mul'
from compute import mul, pow</code></code></pre><p>This means Mojo doesn&#8217;t find the <code>mul</code> function. What&#8217;s still needed is to expose the functions from the <code>compute</code> module in the <code>__init__.mojo</code> file as follows:</p><pre><code><code>from .compute import mul, pow</code></code></pre><p>This is called re-exporting the module&#8217;s symbols: <code>.compute</code> here represents the local (<code>.</code>) <code>compute</code> module. Such relative imports only work inside packages.</p><p>This makes your APIs (like <code>mul</code> and <code>pow</code> here) accessible from the package name <code>compute</code>.<br>What we have done can be called importing modules to the package scope.</p><p>Here you could decide not to expose functions or structs that are private to the modules, such as pure helper functions. The names of private functions or structs start with an <code>_</code>.</p><p>We finally get the correct results:</p><pre><code><code>$ mojo main.mojo
# =&gt; 294 230539333248</code></code></pre><p>Here the package and module are both named <code>compute</code>, but this could be different, especially when there are several modules in the package.</p><p>An alternative way is to leave <code>__init__.mojo</code> empty. Then you have to write the import in <code>main.mojo</code> in the qualified form <code>package.module</code> as:</p><pre><code><code>from compute.compute import mul, pow</code></code></pre><p>The presence of <code>__init__.mojo</code> is crucial here. If you delete it, then Mojo doesn&#8217;t recognize the directory <code>compute</code> as a package and it cannot be imported. So, a Mojo package is a directory containing an <code>__init__.mojo</code> file (which can be empty). This tells Mojo to treat the directory as a package.</p><p>Nested packages (subfolders with their own <code>__init__.mojo</code>) can also be used, just like in Python. This allows you to organize your code hierarchically.</p><p>Instead of importing from source files, we can also make a binary package, as we&#8217;ll see now.</p><div><hr></div><h1>Importing a compiled package</h1><p>A more efficient way to work with packages is to compile the package into a <code>.mojopkg</code> or <code>.&#128230;</code> file. That&#8217;s easier to share than a bunch of source files, while being still compatible with other system architectures. Moreover, compiling module code into binary packages will get you faster start-up and execution.</p><h2>Compiling a package</h2><p>Compile a package&#8217;s <code>package1</code> source code into a package file with the <code>mojo package</code> command like this:</p><pre><code><code>mojo package package1 -o package1.mojopkg</code></code></pre><p><code>package1.mojopkg</code> is the name of the output binary.<br>In our example this would become:</p><pre><code><code>mojo package compute -o compute.mojopkg</code></code></pre><p>This creates the package file <code>compute.mojopkg</code>.</p><p>The code in a <code>.mojopkg</code> file is parametric bytecode, obtained by compiling Mojo source code with the JIT part of the compiler.</p><p>The filename which you specify with the <code>mojo package</code> command can differ from the package directory name. Be careful what you use as the package name in the <code>from package import &#8230;</code> statement:</p><ul><li><p>When importing from source files, the directory name must be used as the package name.</p></li><li><p>When importing from a compiled package, the filename (without the <code>.mojopkg</code> extension) is the package name.</p></li></ul><p>To specify a new name, simply run <code>mojo package</code> again with the new output name. Make sure that the name of the package file and the imported name in <code>main.mojo</code> stay the same.</p><p>Now the user doesn&#8217;t need to have the source code on their machine anymore. The package code can be moved elsewhere (say into a folder <code>production</code>), and the project structure looks like this:</p><pre><code><code>/production
    compute.mojopkg
    main.mojo</code></code></pre><p>Executing <code>mojo main.mojo</code> still produces the same output.</p><h2>Building the project</h2><p>If you want to get rid of the binary package file as well and compile everything into one executable app, use the command:</p><pre><code><code>mojo build main.mojo -o app</code></code></pre><p>Then <code>./app</code> gives the same output as before, even when the <code>.mojopkg</code> file is removed.<br>Now your project can be distributed in only one file:</p><pre><code><code>/production
    app</code></code></pre><p>Test this by copying the <code>app</code> file in a completely different folder and execute <code>./app</code> to see the same output (you probably will have to do a <code>chmod u+x app</code>).<br>Note that when building, the binary code becomes architecture specific.</p><div><hr></div><h1>Using pixi as Mojo&#8217;s package manager</h1><p>Currently many external modules are being written by members of the Mojo community. The official repository for these modules is located <strong><a href="https://github.com/modular/modular-community">here</a></strong>.</p><p>The modules published there are being reviewed and must contain a test suite to ensure their quality. You can also view the featured community packages in a more readable format <strong><a href="https://builds.modular.com/?category=packages">here</a></strong>.</p><p>Whether you want to install, submit, or review packages, this <strong><a href="https://forum.modular.com/t/getting-started-with-community-packages/219">forum post</a></strong> can get you started.</p><p>The pixi tool that we know from installing the Modular environment (see <strong>Appendix A</strong> found at the end of this article) is also used to install modules from this repo into your local environment. To follow along, make sure you&#8217;re in the <code>modules_and_packages</code> folder, and after typing <code>pixi shell</code>, you get the following prompt:</p><pre><code><code>(modules_and_packages) user@computer:~/mojo_book/modules_and_packages$</code></code></pre><p>As an example, we will install the <code>lightbug_http</code> package. Execute the following steps:</p><ol><li><p>Add the Modular community channel (<code>https://repo.prefix.dev/modular-community</code>) to your <code>pixi.toml</code> file in the channels section:</p></li></ol><pre><code><code>[project]
authors = ["Author data"]
channels = [
  "https://conda.modular.com/max-nightly",
  "https://conda.modular.com/max",
  "conda-forge",
  "https://repo.prefix.dev/modular-community",
]
name = "modules_and_packages"
platforms = ["linux-64"]
version = "0.1.0"

[tasks]

[dependencies]
max = "*"</code></code></pre><ol start="2"><li><p>Pixi allows you to browse for packages from the above channels by typing <code>pixi search light*</code> on your command-line:</p></li></ol><pre><code><code>$ pixi search light*
Using channels: https://conda.modular.com/max-nightly/, https://conda.modular.com/max/, conda-forge, https://repo.prefix.dev/modular-community/
Package                                  Version             Channel
lightly                                  1.5.20              conda-forge/noarch
lightfm                                  1.17                conda-forge/linux-64
&#8230;
lightbug_http                            0.1.19              https://repo.prefix.dev/modular-community//linux-64
&#8230;</code></code></pre><ol start="3"><li><p>To install the chosen package, enter the following command:</p></li></ol><pre><code><code>$ pixi add lightbug_http
V Added lightbug_http &gt;=0.1.19,&lt;0.2</code></code></pre><p>Verify with the <code>pixi list</code> command that the package is installed locally in your project.</p><ol start="4"><li><p>From here you can import any functionality you need from the package with import statements as previously discussed, for example:</p></li></ol><pre><code><code>from lightbug_http.service import HTTPService</code></code></pre><div><hr></div><h1><strong>Appendix A</strong></h1><h2>Setting up a development environment</h2><p>Follow the steps below and you&#8217;ll start coding with Mojo in no time.<br>The Mojo tools are completely free forever and will eventually be open-sourced.</p><blockquote><p><strong>NOTE</strong><br>Currently, the Mojo toolchain works on Linux (Ubuntu and other distros) and on macOS (Apple Silicon). On Windows, everything works fine within the WSL2 environment. Native support for Apple (Intel) and Windows is coming.</p></blockquote><div><hr></div><h2>A.1 Installing Pixi</h2><p>Installing Mojo locally is a quick and easy process. We only need to <a href="https://docs.modular.com/mojo/manual/get-started/">get pixi and the modular package</a>.<br>Use the following command from a terminal if pixi doesn&#8217;t exist yet on your machine:</p><pre><code><code>$ curl -fsSL https://pixi.sh/install.sh | sh</code></code></pre><p>Now the pixi binary is in the folder <code>$HOME/.pixi/bin</code>, where <code>$HOME</code> equals <code>/home/username</code> or <code>~</code>, and the command is system-wide available.</p><p>Test it by showing its version:</p><pre><code><code>$ pixi -V</code></code></pre><p>Output, for example:</p><pre><code><code>pixi 0.48.0</code></code></pre><p>If you need to get the latest version, do:</p><pre><code><code>$ pixi self-update</code></code></pre><p>Removing pixi is as easy as:</p><pre><code><code>$ rm ~/.pixi/bin</code></code></pre><p><code>$ pixi -h</code> shows all available commands.</p><blockquote><p><strong>NOTE:</strong><br>If you&#8217;re working in a newly created Linux/macOS or WSL environment, make sure to install:</p><pre><code><code>$ sudo apt-get install -y git gcc g++ zlib1g-dev libtinfo-dev</code></code></pre></blockquote><h3>What is Pixi?</h3><p><strong><a href="https://pixi.prefix.dev/latest/">Pixi</a></strong> is a virtual environment manager and works also as package manager for Mojo, Python, and other languages, kind of like what cargo is for Rust developers.</p><h3>Why use a virtual environment?</h3><p>Pixi allows you to create fully contained projects in a so-called virtual environment (venv), which is a separate environment (in a folder) which contains the exact versions of all packages you need.</p><p>In a venv, the package dependencies and environment settings are automatically managed for you. In fact, any Mojo project is created in a virtual environment. What is the advantage of working with this? In the global (machine-wide) environment, you can have only one version of a package installed, say numpy v 2.0. Suppose you need to maintain a project which uses numpy v 1.8. This cannot work globally, so you have to create a closed virtual environment containing numpy 1.8, in which you can maintain your project. This venv can contain particular versions of Mojo and of any package your project needs. Now you can work on your project without any conflicts with the global environment or any other virtual environments you create.</p><p>Let&#8217;s illustrate this more concretely by creating a Mojo project.</p><div><hr></div><h2>A.2 Creating a Mojo project</h2><p>To keep all your Mojo projects in one place, you probably want to create a folder (any name will do) like <code>mojo_projects</code>, and <code>cd</code> into it.</p><p>The following command creates a Mojo project named <code>project1</code> with <code>pixi init</code>:</p><pre><code><code>$ pixi init project1 -c "https://conda.modular.com/max-nightly" -c "https://repo.prefix.dev/modular-community" -c "conda-forge"</code></code></pre><p>This displays the following output:</p><pre><code><code>Created $HOME/mojo_projects/project1/pixi.toml</code></code></pre><p>A folder <code>project1</code> is created, git version control-ready (<code>cd</code> into it and check with <code>$ ls -al</code>). It contains the configuration file (also called the manifest) <code>pixi.toml</code>, which contains:</p><pre><code><code>[workspace]
authors = ["Username &lt;email-adress&gt;"]
channels = ["https://conda.modular.com/max-nightly", "https://repo.prefix.dev/modular-community", "conda-forge"]
name = "project1"
platforms = ["linux-64"]
version = "0.1.0"

[tasks]

[dependencies]</code></code></pre><p>The <code>channels</code> field in <code>[workspace]</code> contains all locations where pixi can find and download the files that are necessary to run your Mojo code.</p><p>In the <code>[tasks]</code> section, you can define different tasks to be executed for your project, like testing. The <code>[dependencies]</code> section names the versions of all packages your code depends on.</p><blockquote><p><strong>Note</strong><br>In the <code>pixi init</code> command we used the channel <code>"https://conda.modular.com/max-nightly"</code>. This gives you the nightly update of the Modular package and Mojo compiler, containing the latest new features and performance improvements. If you want to stay in sync with the rapid development of Mojo, this is ok. Be warned that this version can be unstable, and changes very frequently, so this is not to be used for a project in production!<br>On the other hand, if you want to stay synchronized with a stable version of Mojo, say v. 25.4, you should use the channel <code>"https://conda.modular.com/max"</code> instead.</p></blockquote><p>With this setup, Mojo works only inside the directory created with <code>pixi init</code>, which is great for project development. If you want the latest stable Mojo to be system-wide available, you can install it globally with:</p><pre><code><code>$ pixi global install max -c conda-forge -c https://conda.modular.com/max/ --expose mojo</code></code></pre><p>If you want to create a pixi project in an existing folder, just go into that folder and issue the previous command, leaving out the project name <code>project1</code>.</p><h3>Installing the Modular platform</h3><p>This can be done in two ways:</p><ol><li><p>Execute the command:</p></li></ol><pre><code><code>$ pixi add modular</code></code></pre><p>This downloads and installs the modular package in your folder. When it is finished, it displays:</p><pre><code><code>&#10004; Added modular &gt;=25.4.0.dev2025060721,&lt;26</code></code></pre><p>Check that this dependency was added to the <code>pixi.toml</code> file!<br>The MAX engine, which is registered as a conda project, is now installed. Mojo is bundled together with MAX, so it is available as well!</p><p>The command also has added the following:</p><ul><li><p>A <code>.pixi</code> subfolder, which contains the complete virtual environment for your Mojo project in the folder <code>envs</code>.</p></li><li><p>A <code>pixi.lock</code> file</p></li></ul><p>This big file contains in detail all dependencies, namely the exact versions and the dependencies of the dependencies (the transitive dependencies). This ensures that you get the exact same environment when building your project on another machine.</p><p>If you&#8217;re curious, look inside it with the grep command: <code>grep -R 'word' pixi.lock</code>, for example: <code>grep -R max pixi.lock</code>.<br>Do not edit/change it, because this is an auto-generated file!</p><p>Let&#8217;s verify the Mojo version we have installed in our project environment with the following command (<code>pixi run</code> executes a command in the venv):</p><pre><code><code>$ pixi run mojo --version</code></code></pre><p>This should display something like:</p><pre><code><code>Mojo 25.4.0.dev2025060721 (b35d3649)</code></code></pre><p>Where <code>devnnn</code> denotes the latest Mojo nightly file.</p><ol start="2"><li><p>Another way to install the platform is by editing the <code>pixi.toml</code> file, and adding a line to the <code>[dependencies]</code> section of the format <code>"package = version"</code>, in our case:</p></li></ol><pre><code><code>max = "*"</code></code></pre><p>The <code>max</code> package contains the <code>mojo</code> package, so this gets us Mojo as well. <code>"*"</code> means: get the latest (nightly) version.</p><p>The version could also be indicated as:</p><ul><li><p><code>"==25.4"</code>: which gets you that exact version.</p></li><li><p><code>"&gt;=25.4"</code>: meaning, install the latest version in &#8220;any version from 25.4&#8221; or later.</p></li><li><p><code>"&gt;=25.2, &lt;25.4"</code>: meaning, install version 25.2 or later, but less than 25.4.</p></li><li><p><code>"~=25.4"</code>: this adds a version of Max and Mojo, namely v25 and compatible versions.</p></li></ul><p>When this info is saved, run the following command in a terminal:</p><pre><code><code>$ pixi install</code></code></pre><p>Both ways will add this dependency info also to the <code>pixi.lock</code> file.</p><blockquote><p><strong>NOTE:</strong><br>In a future version, the subfolder <code>.pixi</code> will contain all dependencies not physically as it does now, but as links. Only one copy of every version of every dependency is kept on your machine in a system cache, and each project only links to the copies it needs.</p></blockquote><div><hr></div><h2>A.3 Working with the pixi shell</h2><p>You can run a Mojo program outside the pixi shell with:</p><pre><code><code>$ pixi run mojo hello_world.mojo</code></code></pre><p>But it is much easier to work inside the shell using simpler commands. To enter this shell do:</p><pre><code><code>$ pixi shell</code></code></pre><p>This will show you a new prompt, which starts with your project name, like:</p><pre><code><code>(project1) username$computername: folder-path$</code></code></pre><p>Now you can work with any mojo command. For example, check the version of Mojo and MAX with:</p><pre><code><code>$ mojo -v</code></code></pre><pre><code><code>mojo 24.6.0 (4487cd6e)</code></code></pre><p>Use <code>exit</code> to leave the pixi shell to the OS prompt.</p><h3>Some other useful Pixi commands</h3><p>Here are some of the most used commands:</p><h4>1) Add a dependency</h4><p>This is done through the <code>add</code> command. For example, to add a specific Python version to your Mojo project you can use:</p><pre><code><code>$ pixi add "python==3.10"</code></code></pre><p>Try it out and examine the change in the <code>.toml</code> configuration file.<br>Use the same command to add one (or more) Python packages:</p><pre><code><code>$ pixi add "python==3.10" "numpy&lt;2.0"</code></code></pre><p>This adds python v 3.10 and the latest version of NumPy that&#8217;s less than 2.0.</p><p>An alternative way is to add these dependencies to <code>pixi.toml</code> as:</p><pre><code><code>python==3.10
numpy&lt;2.0</code></code></pre><p>and then run <code>pixi install</code> again.</p><p>If the package is not available in conda-forge, you must add its channel to the channels list in <code>pixi.toml</code>.</p><h4>2) Update a dependency</h4><p>Use the <code>pixi update packagename</code> command, for example:</p><pre><code><code>$ pixi update max</code></code></pre><h4>3) Clean the local project environment</h4><p>The command <code>pixi clean</code> will delete the current environment, without affecting the manifest <code>.toml</code> file.<br><code>pixi install</code> then installs the latest versions. The neat effect is to remove any outdated dependencies. This avoids clutter and keeps your project running fluently.</p><h4>4) Starting from a Python project</h4><p>Add the desired Python version to the project folder with:</p><pre><code><code>$ pixi add "python==3.12"</code></code></pre><p>How would you add Mojo to such a project?<br>Run:</p><pre><code><code>$ pixi add max</code></code></pre><p>For more details on what Pixi can do, see chapter 10 and the official documentation (<a href="https://pixi.sh/latest/getting_started/">https://pixi.sh/latest/getting_started/</a>).</p><p>You can also install the Modular software as a Python package in a Python environment with:</p><pre><code><code>$ pip install modular</code></code></pre><p>(<a href="https://docs.modular.com/max/get-started/">https://docs.modular.com/max/get-started/</a>)</p><div><hr></div><h2>A.4 Installing Visual Studio Code, the Mojo extension and Jupyter notebooks</h2><p>Plugins exist for the vim and PyCharm editors, but the most popular development platform for Mojo is Visual Studio Code (VS Code) (</p><p>https://code.visualstudio.com/</p><p>). Installing this is straightforward. It works on every OS; notably, the Windows integration with WSL2 is superb.</p><p>Then install the official plugin (developed and maintained by the Modular team) called <code>vscode-mojo</code> from:<br><a href="https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo">https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo</a><br>or search for &#8220;Mojo&#8221; in the &#8220;Extensions: Marketplace&#8221; tab.</p><p>Together with its integrated LSP (Language Server), it gives you a ton of functionalities.</p><blockquote><p><strong>Note</strong><br>Two versions of the extension exist: Mojo and Mojo (nightly). The first one is the stable version. Synchronize this with the version you use in your Mojo projects.</p></blockquote><p>We&#8217;ll start using it in &#167; 1.4.2.<br>The Mojo LSP server can also be used in other editors that support LSP.</p><p>To work with Jupyter notebooks in VS Code, you need to install the Jupyter VS Code extension from:<br><a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter">https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter</a></p><p>Here are the steps to install Jupyter notebook in a project:<br>Within your pixi shell, add Python with, for example:</p><pre><code><code>$ pixi add "python==3.12"</code></code></pre><p>Then run the command:</p><pre><code><code>$ pip install notebook</code></code></pre><p>For Mojo you need to install the following libraries:</p><pre><code><code>$ pixi add ipykernel jupyterlab</code></code></pre><div><hr></div><h2>A.5 Moving from magic to Pixi</h2><p>In an existing magic project:</p><ul><li><p>do <code>magic clean</code></p></li><li><p>delete the <code>.magic</code> folder and <code>magic.lock</code></p></li><li><p>rename <code>mojoproject.toml</code> to <code>pixi.toml</code></p></li><li><p>do <code>pixi install</code></p></li></ul><div><hr></div><h2>A.6 Where to find more information</h2><p>For more information, you can access the <strong><a href="https://github.com/modular/max">GitHub repository</a></strong> for Mojo and Max or the <strong><a href="https://docs.modular.com/mojo/manual/">official docs</a></strong>. </p><p>You can also contact other Mojo users or the Modular team on the <strong><a href="https://discord.com/invite/modular">Discord channel</a></strong> or on the <strong><a href="https://forum.modular.com/">Forum</a></strong>. Discord even has an AI-chatbot specialized in Mojo code to answer your queries or problems. Just type <code>@kap</code> and choose the value <code>@kapa.ai</code> from the list, then state your question after that (see the channel <code>mojo-bot-help</code>).</p><p>A <strong><a href="https://www.reddit.com/r/ModularAI/?rdt=40723">Reddit channel</a></strong> and a <strong><a href="https://www.modular.com/community">community website</a></strong> are also available. For broader info, you can consult <strong><a href="https://www.modular.com/">Modular&#8217;s website</a></strong>.</p><p>The <strong><a href="https://builds.modular.com/">MAX Builds page</a></strong> is also live and contains examples of implemented AI models, recipes, and top community packages, which you can view <strong><a href="https://builds.modular.com/?category=packages">here</a></strong>. Their official repo is maintained <strong><a href="https://github.com/modular/modular-community">here</a></strong>.</p><p>An overview of educational material and links to community projects can be found <strong><a href="https://github.com/mojicians/awesome-mojo">here</a></strong>.</p><h3>Info about local installation:</h3><p>The pixi executable is stored centrally at: <code>$HOME/.pixi/bin</code></p><p>Currently (June 2025) a project&#8217;s dependencies are still physically contained in the <code>.pixi</code> folder of every project (more precisely in <code>$HOME/projectname/.pixi/envs/default/</code>):</p><ul><li><p>The mojo cache and <code>modular.cfg</code> file is stored in the subfolder <code>share/max</code>.</p></li><li><p>The <code>mojo</code>, <code>mojo-lldb</code>, and <code>mojo-lsp-server</code> executables are stored in the <code>bin</code> subfolder.</p></li><li><p>The Mojo/Max packages are stored at the <code>lib/mojo</code> subfolder.</p></li></ul><div><hr></div><p>&#169; 2026 Ivo Balbaert. All rights reserved.</p><div><hr></div>]]></content:encoded></item><item><title><![CDATA[Building with Mojo (Part 4): Compile-Time Metaprogramming in Mojo]]></title><description><![CDATA[How aliases, parameters, and decorators push work to compile time for faster, safer Mojo programs.]]></description><link>https://deepengineering.net/p/building-with-mojo-part-4-compile</link><guid isPermaLink="false">https://deepengineering.net/p/building-with-mojo-part-4-compile</guid><dc:creator><![CDATA[Ivo Balbaert]]></dc:creator><pubDate>Wed, 12 Nov 2025 08:40:30 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0a36e862-d55c-4c0c-b4b3-f1621b2cdc2b_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This article is Part 4 of our ongoing series on the Mojo programming language. <strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language">Part 1</a></strong> introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;8ee18aa1-33e9-47a6-b9d9-8f6d36f98def&quot;,&quot;caption&quot;:&quot;This article, kicks off a multi-part series exploring Mojo, a new programming language developed by the AI company Modular. Mojo is designed for AI-scale performance without abandoning Pythonic ergonomics. In Part 1, we&#8217;ll cover the following topics:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 1): A Language Born for AI and Systems&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-07-16T10:22:41.479Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e127b69e-ec68-43db-88a6-294f5ee6343d_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:168455891,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:1,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd">Part 2</a></strong> covered </em>Mojo&#8217;s SIMD-first model in practice.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;51a0a5bc-0943-43ad-a1c1-b2be16b13c07&quot;,&quot;caption&quot;:&quot;This article is Part 2 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 2): Using SIMD in Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-09-11T08:28:42.258Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79fa439e-7d92-49ac-b0a2-2be7b06591cf_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:173251283,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-3-python">Part 3</a></strong> covered converting a Python program to Mojo, and exploring how they interact.</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;1ad0498b-0e02-447b-be9b-840d6de60458&quot;,&quot;caption&quot;:&quot;This article is Part 3 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 3): Python and Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-10-09T07:48:01.963Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f602cbb-615d-4272-91c3-2e88d5092315_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-3-python&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:175610468,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></blockquote><p>In this article, we will discuss Mojo&#8217;s possibilities to do metaprogramming, which is possible at compile time. Let&#8217;s first explain what we mean by metaprogramming.</p><p>To follow along, use pixi to create a project called <code>comptime</code>, <code>cd</code> into it, and start a pixi shell.</p><blockquote><p><strong>All code examples used in this article can be found in the code repo <a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/tree/main/6_Using_Functions">here</a> and <a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/7_Structs_and_Traits/planets_param.mojo">here</a>.</strong></p></blockquote><h1>What is metaprogramming and why is it important?</h1><p>Metaprogramming is about transforming or generating code at compile time. The new or changed code is then compiled and executed at run time.</p><p>One of the great characteristics of Python is that you can change code at run time&#8212;so-called run-time metaprogramming. This can do some amazing things, but it comes at a great performance cost.</p><p>The modern trend in programming languages is toward statically compiled languages, with metaprogramming done at compile time. Think about Rust macros, Swift, Zig, and Jai. Mojo, as a compiled language, fully embraces compile-time (comptime) metaprogramming, built into the compiler as a separate stage of compilation&#8212;after parsing, semantic analysis, and IR generation, but before lowering to target-specific code. To do this, the same Mojo language is used for metaprograms as for run-time programs, and it also leverages MLIR. This is because work in AI needs high-performance machine learning kernels and accelerators, and high abstraction capabilities provided by advanced metaprogramming systems. You can also write imperative compile-time logic with control flow, even compile-time recursion.</p><p>Currently, the following techniques are used in Mojo to do metaprogramming:</p><ul><li><p>Defining aliases at comptime. These are constants, so they won&#8217;t change during the execution of the program.</p></li><li><p>Running arbitrary Mojo code at comptime to set parameter values with an alias.</p></li><li><p>Checking a function constraint at comptime with a <code>constrained</code> statement.</p></li><li><p>Using the decorators <code>@parameter for</code> and <code>@parameter if</code> to run the <code>for</code> and <code>if</code> loop they mark at comptime. The for-loop is unrolled at comptime and the if-condition is checked at comptime, so that code is only generated for the branch that evaluates to true. Use <code>@parameter</code> to make a function run at comptime and turn it into a parametric closure.</p></li><li><p>Using <code>@always_inline</code> to force a function to be always inlined. The body of the function, with parameter values inserted, is then placed at the function call site.</p></li><li><p>Functions and structs with overloading on parameters: using parameters in functions to transfer part of the computation to comptime, so that execution time is reduced.</p></li><li><p>All Python processing happens at comptime; that is, all interaction between the Python interpreter and the Mojo compiler happens at comptime. Python code is processed at comptime by the CPython interpreter.</p></li><li><p>Note that memory used by Mojo is not freed by a process at run time like a garbage collector (GC). Instead, the Mojo compiler, while analyzing how data is manipulated through code, inserts the code to free memory in the executable during comptime.</p></li></ul><p>So we see that a lot of preparation and processing can be done at comptime, leading to speed gains at run time. For example: a function can be executed at comptime and keep its results for use at run time.</p><p>We&#8217;ll explore examples of these techniques in the rest of the article, but first let us clear up the difference between comptime and run time.</p><h1>Compile time and run time</h1><p>Python, being interpreted, has only one phase, namely run time: it is interpreted while it is running. But Mojo, as a compiled language (with <code>mojo build</code>), splits compilation from execution, so it has two phases:</p><ul><li><p><strong>Compile time</strong> (abbreviated to comptime) is when the compiler scans your program and generates errors or warnings. If no errors are found, an executable (containing machine code) is generated. Data that is known at comptime is said to be statically known.</p></li><li><p><strong>Run time</strong> is when the executable runs on your machine. Often, data is not known at comptime, but only at run time&#8212;for example, when it is read in from a network or calculated with input during execution. Such data is said to be dynamically known.</p></li></ul><p>Mojo code can also run at compile time, following the trend in many other modern statically compiled languages.</p><p>Everything that can be done at comptime has a performance benefit because it saves run time. This is a form of metaprogramming, which is extensively used in Mojo. Python can also do metaprogramming, but only at run time. Mojo can simulate some types of dynamic programming, but it can do a lot more as well.</p><p>Now we&#8217;ll show example code using some of these techniques.</p><h1>Aliases and running functions at comptime</h1><p>Executing code at comptime is one of the key aspects of Mojo&#8217;s performance. It lowers the amount of code to be executed at run time. This can be done in a flexible way: functions can be annotated with decorators such as <code>@parameter if</code> and <code>for</code> to make them run at comptime and preprocess the code to execute.</p><p>When the argument values of a function are already known at the time of writing the code (and thus also at comptime), we might as well execute the function at comptime, saving the results in the executable for use at run time. Think of doing a complex calculation or building a data structure and shaving off quite a bit of the execution time!</p><p>As we saw in <em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language">Part 1</a></strong></em>, an alias can be defined like this:</p><pre><code><code>alias SUM = sum(10, 20, 2)  # A

fn sum(lb: Int, ub: Int, step: Int) -&gt; Int:</code>
<code>    var total = 0</code>
<code>    for i in range(lb, ub, step):</code>
<code>        total += i</code>
<code>    return total</code>

<code>fn main():</code>
<code>    print(SUM)               # =&gt; 70</code>
<code>    print(sum(10, 20, 2))    # =&gt; 70      # B</code></code></pre><p><code>#A</code> <strong>SUM</strong> is calculated by executing the <code>sum</code> function at comptime.<br><code>#B</code> The same sum is calculated at run time.</p><p>An alias is a comptime constant. A variable, on the other hand, exists only at run time and has a limited lifetime (scope) in which it is known.</p><p>The right-hand side of an alias declaration is executed at comptime. The line <code>print(SUM)</code> compiles to the code <code>print(70)</code>, which is displayed at run time. The result is that there is less to compute at run time, which makes the code more performant.</p><p>The function <code>sum</code> can be used both during comptime and run time. The alias <code>SUM</code> ensures that the calculation is done at comptime, making it usable at run time. The result is stored as an alias constant in the executable file. This means that when the program is run, it just takes the alias constant.</p><p>Aliases are used a lot in code, also as fields inside structs and traits. They can even be used to set a type at compile time, like this:</p><pre><code><code>alias dtype = DType.float32</code></code></pre><p>The way <code>DType</code> is used here creates specialized code at comptime, optimized for the type <code>Float32</code>.</p><p>Not every function can be run at comptime, however. To be able to do that, the function must be:</p><ul><li><p>an <code>fn</code>-type function (so a <code>def</code> function cannot be run at comptime);</p></li><li><p>a pure function. Such functions have no side effects; that is, they can only use the arguments passed to them and cannot change any variables or state outside of the function body.</p></li></ul><p>Functions can also be conditionally executed at comptime, either by testing known or computed alias values or checking whether a certain name is defined at the command line.</p><h1>Conditional execution at comptime</h1><p>A function at comptime can also be called conditionally using an <code>if/else</code> test on values that are known at comptime. Working further on the example of the previous section, let&#8217;s replace the line <code>alias SUM = sum(10, 20, 2)</code> with the following code (<a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/6_Using_Functions/alias_calc_condition.mojo">see</a><strong><a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/6_Using_Functions/alias_calc_condition.mojo"> </a></strong><code>alias_calc_condition.mojo</code>):</p><pre><code><code>alias lb = 10
alias ub = 20
alias step = 2
alias SUM = sum(lb, ub, step) if lb &lt; ub else 0   # A
# A Call of sum() at comptime is dependent on the values of lb and ub</code></code></pre><p>Now the <code>sum</code> function will only be called when the condition <code>lb &lt; ub</code> is met. Replace the value of <code>lb</code> in the above code with <code>50</code>, and see that <code>SUM</code> now becomes <code>0</code> instead of <code>70</code>.</p><p>Another trick is to make the comptime execution of a function dependent on a command-line variable provided with <code>-D</code>. In the following version (see <code>alias_calc_conditionD.mojo</code>) we start the program with:</p><pre><code><code>$ mojo -D calc_sum alias_calc_conditionD.mojo</code></code></pre><p>The result for <code>SUM</code> is still <code>70</code>. The functions <code>main()</code> and <code>sum()</code> remain the same, but now the alias condition is changed to:</p><pre><code><code>from sys import is_defined       # A

alias lb = 10
alias ub = 20
alias step = 2
alias SUM = sum(lb, ub, step) if is_defined[&#8221;calc_sum&#8221;]() else 0  # B
# A The function is_defined is imported from the sys module
# B is_defined is called here: this tests if a variable calc_sum is defined on the command line</code></code></pre><p>The line <code>is_defined[&#8221;calc_sum&#8221;]()</code> checks whether a command-line variable <code>calc_sum</code> is provided after the <code>-D</code> option flag. Only then is the function <code>sum(lb, ub, step)</code> called.</p><p>The more info a function can get at comptime, the more useful its execution at comptime becomes. Mojo introduces <strong>parameters</strong> for that purpose: they are used at comptime, in contrast to function arguments which are used at run time.</p><p>Mojo function parameters work like arguments, but they must be known at comptime. They also allow for more generic and flexible functions. Understanding parameters is one of the secrets any upcoming Mojo developer must master: they are pervasive in stdlib code and commonly used in heavy calculations. Let&#8217;s dig deeper into them.</p><h1>Using parameters in functions</h1><p>Another powerful tool for running functions is <strong>comptime parameterization</strong>. This technique (that originated in Zig) uses a list of parameters provided between <code>[]</code> in the function header. These parameters must be (or reduce to) constant values at comptime and are used inside the function&#8217;s code. Using them, the compiled code for the function can be simplified, so that run-time execution performs better. We will start with a simple parameter example.</p><h2>A function with a parameter</h2><p>In Listing 1, we see a <code>greet</code> function that prints out a message variable one time. Suppose we want to greet several persons at the same time. Using a <code>for</code>-loop around <code>greet</code> would be an obvious solution, and we can pass the number of greetings as a variable <code>count</code>, as we&#8217;ve done in <code>fn greet_repeat_args</code>. But an alternative is to pass <code>count</code> as a parameter between <code>[]</code>, as we&#8217;ve done in <code>fn greet_repeat[count: Int](msg: String)</code> (see <code>fn_param.mojo</code>):</p><blockquote><p><strong>Listing 1 &#8212; A function with a parameter</strong></p></blockquote><pre><code><code>fn greet(msg: String):
    print(msg, end=&#8221; / &#8220;)

fn greet_repeat_args(count: Int, msg: String):  # A
    for _ in range(count):
        greet(msg)

fn greet_repeat[count: Int](msg: String):       # B
    for _ in range(count):
        greet(msg)

fn main():
    greet(&#8221;How are you?&#8221;)
    print()
    greet_repeat_args(3, &#8220;Hello there!&#8221;)        # C
    print()
    greet_repeat[3](&#8221;Hello there!&#8221;)             # D

# =&gt;
How are you? /
Hello there! / Hello there! / Hello there! /
Hello there! / Hello there! / Hello there! /</code></code></pre><p><code>#A</code> The function <code>greet_repeat_args</code> has two arguments <code>count</code> and <code>msg</code>.<br><code>#B</code> The function <code>greet_repeat</code> has a <code>count</code> parameter between <code>[]</code>.<br><code>#C</code> Arguments are passed between <code>()</code>.<br><code>#D</code> The parameter <code>count</code>&#8217;s actual value is passed between <code>[]</code>.</p><p>The output is the same, so why bother? Everything passed to a function between <code>[]</code> is a compile-time parameter (like <code>count</code> in our example). This parameter becomes a run-time constant. The compiler makes a specific version of <code>greet_repeat</code> with the <code>count</code> value set to <code>3</code>, like this:</p><pre><code><code>fn greet_repeat(msg: String):
    for _ in range(3):
        greet(msg)</code></code></pre><p>which is then compiled and executed at run time. You can see that the parameter presets a value in the code. The code for <code>fn greet_repeat_args</code> is more complex at run time than <code>greet_repeat</code>, because it still must process two arguments. In this simple example, this doesn&#8217;t visibly influence performance, but for complex functions with many parameters these preset values may make a big difference in run-time execution&#8212;specifically when the parameter contains a type as value: then the run-time code can be optimized for that type.</p><p>(Note that the iteration variable in the <code>for</code>-loop is written as <code>_</code>; we don&#8217;t need to create a variable here because it is not used in the loop&#8217;s body.)</p><p>Now we know that Mojo can do computations at two different times of execution: comptime and run time, and data can be used at comptime or at run time:</p><ul><li><p><strong>Parameters</strong> (enclosed between <code>[]</code>) are processed at comptime and become a static (constant) value to be used at run time.</p></li><li><p><strong>Arguments</strong> (enclosed between <code>()</code>) are processed at run time and become a run-time (or dynamic) value.</p></li></ul><p>A function header schematically looks like this:</p><pre><code><code>fn fun_name[parameter_list](argument_list) -&gt; return_type:</code></code></pre><p>The parameter list is optional as well as the return type, and the argument list can be empty.</p><p>Parametric code gets compiled at comptime, not JIT-compiled (Just-In-Time compiled) at run time. Multiple specialized versions of the code are generated, parameterized by the concrete types used during program execution.</p><p>In general, if you encounter the error <code>cannot use a dynamic value in a type parameter</code>, this means that you used a run-time value as a compile-time parameter.</p><blockquote><p><strong>TIP</strong></p><p>The word <em>parameter</em> has a totally different meaning in Mojo than in most other programming languages, where the defined versions of a function&#8217;s arguments in its signature are called the function&#8217;s parameters. For example, in this Java function header: <code>public int add(int a, int b)</code>, <code>int a</code> and <code>int b</code> are called the parameters of function <code>add</code>. When the function is called, like in <code>add(3,5)</code>, <code>3</code> and <code>5</code> are the arguments. In contrast, in Mojo, <strong>parameters are comptime values</strong>, and <strong>arguments are run-time values</strong>.</p></blockquote><p>Parameters also can have default values, for example: <code>fn greet_repeat[count: Int = 2](msg: String)</code>. Just like variadic arguments, you can pass a variable number of parameters like this: <code>fn add_all[*a: Int]() -&gt; Int</code>, which can be called as: <code>add_all[1, 2]</code> or <code>add_all[1, 2, 3, 4, 5]</code> or <code>add_all[]</code>.</p><h2>The <code>@parameter for</code> decorator</h2><p>By adding a <code>@parameter</code> decorator, we can significantly improve performance by evaluating the <code>for</code>-loop entirely at compile time. In the function <code>greet_repeat</code> in the previous example, the range was known at comptime through the parameter, but the <code>for</code>-loop itself still had to be executed at run time, like this:</p><pre><code><code>fn greet_repeat(msg: String):
    for _ in range(3):
        greet(msg)</code></code></pre><p>Mojo can do better: by prefixing the loop with the <code>@parameter</code> decorator like this:</p><pre><code><code>fn greet_repeat(msg: String):
    @parameter                  # A
    for i in range(3):
        greet(msg)
# A The @parameter for decorator unrolls the for loop</code></code></pre><p>Now, the loop will be unrolled. It will be changed to a sequence of statements at comptime as:</p><pre><code><code>fn greet_repeat(msg: String):
    greet(msg)
    greet(msg)
    greet(msg)</code></code></pre><p>This might not seem spectacular here, but when used in complex and nested <code>for</code>-loops, it can have massive implications for run-time speed, like in this code snippet:</p><pre><code><code>@parameter
for i in range(NUM_BODIES):
    for j in range(NUM_BODIES - i - 1):
        var body_i = bodies[i]
        var body_j = bodies[j + i + 1]</code></code></pre><p>A downside of unrolling loops is that the program grows in size before it is compiled, resulting in a slightly increased compilation time and executable size.</p><p>Checking constraints to which functions must comply to run is important to make your code robust. This can be done at comptime with <strong>constrained</strong> testing, or at run time using <code>assert</code> testing. Because this article is targeted to code executed at comptime, we won&#8217;t talk about <code>assert</code> testing in detail here, but there is an example of its use in the code example for <code>@parameter if</code>.</p><h1>Constraints checking in functions</h1><p>Often, we write programs in which we unconsciously suppose that certain values or arguments comply with certain conditions. In certain edge cases, these conditions might not be met, crashing our program while running, which could get us caught in a long debugging session. Now that we know about parameters, the same situation can happen there: a program can crash with a compiler error during comptime. A compiler error might not be so easy to track down, and debugging metaprogramming at comptime is not nearly as nice as normal run-time debugging. We as developers must arm ourselves against these situations, both during run time and during comptime. Luckily, Mojo has our back in this.</p><h2>Checking constraints during comptime</h2><p>A program that crashes because of an incompatible parameter during comptime (while doing metaprogramming) is annoying. When a function has parameters, it would be nice if we could test these parameters on certain conditions. That&#8217;s where <strong>constraints</strong> come in: they are like custom comptime checks you as a developer can add to enhance the robustness of comptime execution.</p><p>Suppose we want to make sure that our machine has an NVIDIA GPU at our disposal, running on an Apple M2 or M3 processor. This can be done as shown in Listing 2 (<a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/6_Using_Functions/constrained_checking.mojo">see </a><code>constrained_checking.mojo</code>):</p><blockquote><p><strong>Listing 2 &#8212; Using </strong><code>constrained</code><strong> to test comptime conditions</strong></p></blockquote><pre><code><code>from sys.info import is_apple_m2, is_apple_m3, has_nvidia_gpu_accelerator

def main():
    machine_checks[]()

def machine_checks[]():
    constrained[has_nvidia_gpu_accelerator(), &#8220;No NVIDIA GPU present&#8221;]()
    constrained[is_apple_m2() | is_apple_m3(), &#8220;Not an Apple M2 or M3 CPU&#8221;]()
    # =&gt; note: constraint failed: Not an Apple M2 or M3 CPU</code></code></pre><p>Running on a machine which has no Apple M2 or M3 processor, the constraint failed on this condition. However, we got no warning about the GPU, so this computer has an NVIDIA GPU.</p><p>Note that <code>constrained</code> has the condition as its first parameter, and the message displayed in case the condition is false as its second parameter.</p><h1>Other decorators for functions</h1><p>Decorators are a powerful and elegant feature in many programming languages (Python; annotations in Java; modifiers in C#; and so on) that allow you to modify the behavior of a function, method, struct, or class without changing its source code. Decorators are another form of metaprogramming, since a part of the program (the decorator) tries to modify another part of the program (for example, the function or struct) at comptime.</p><p>Decorators are no stranger to us: we encountered <code>@register-passable</code> in previous articles. In Mojo, decorators are higher-order functions, prefixed with <code>@</code> and placed in front of the function, struct, or code they influence. At comptime, the higher-order function behind the decorator name is called to modify or extend the code they decorate.</p><p>In this section, we&#8217;ll specifically focus on decorators used for code and functions. In the next section, we&#8217;ll discuss decorators for structs.</p><h2><code>@always_inline</code></h2><p>Inlining a function means taking its code body and replacing the function call with the function body. This improves run-time performance by avoiding function call overhead; it eliminates jumping to a new point in code. But if the function is large and called many times, we have a lot of duplicated code, increasing the program&#8217;s binary size.</p><p>Normally, the compiler will do inlining automatically where it improves performance. But you can force this behavior with <code>@always_inline</code>: this decorator forces the compiler to always inline the decorated function, directly into the body of the calling function.</p><p>In the following example we combine <code>@always_inline</code> with <code>@parameter for</code> (see <code>always_inline.mojo</code>):</p><pre><code><code>@always_inline                          # A
fn print_and_increment(mut x: Int):
    print(x)
    x += 1

fn main():
    var i = 0

    @parameter                          # B
    for j in range(0, 3):
        print_and_increment(i)

# =&gt;
# 0
# 1
# 2
# A @always_inline replaces the function call by the function&#8217;s code
# B @parameter for unrolls the for loop</code></code></pre><p>Applying <code>@always_inline</code> removes the code before <code>main()</code>, so that it is transformed like this:</p><pre><code><code>fn main():
    var i = 0

    @parameter
    for j in range(0, 3):
        print(i)
        i += 1</code></code></pre><p>After applying <code>@parameter for</code>, the code is reduced to:</p><pre><code><code>fn main():
    var i = 0
    print(i)  # =&gt; 0
    i += 1
    print(i)  # =&gt; 1
    i += 1
    print(i)  # =&gt; 2
    i += 1</code></code></pre><p>The program is faster by not checking if <code>j &lt; 3</code> on each iteration of the range and by not having to jump into <code>print_and_increment</code>. It also becomes bigger, but this is a choice you make.</p><p><code>@always_inline</code> is pervasively used in high-performance Mojo code.</p><p>There is a variant <code>@always_inline(&#8221;nodebug&#8221;)</code> which speeds up a bit more by removing the debugging information of the function, so you won&#8217;t be able to debug it anymore. Apply this variant to low-level functions which call MLIR or inline assembly code, which you probably won&#8217;t or can&#8217;t debug.</p><h2><code>@parameter if</code></h2><p>The decorator <code>@parameter if</code> will make an <code>if</code> statement run at compile time. In our example, it is used to define an alias <code>debug_mode</code>. Only when <code>debug_mode</code> is <code>True</code> will the assertion code be included and run in the final executable (<a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/6_Using_Functions/parameter_if.mojo">see </a><code>parameter_if.mojo</code>):</p><blockquote><p><strong>Listing 3 &#8212; Using </strong><code>@parameter if</code><strong> to create a debug mode</strong></p></blockquote><pre><code><code>from testing import assert_true

alias debug_mode = True  # A

fn example():
    @parameter
    if debug_mode:       # B
        print(&#8221;debug&#8221;)

fn main() raises:
    example()            # =&gt; debug

@parameter
if debug_mode:           # C
    _ = assert_true(1 == 2, &#8220;assertion failed&#8221;)
    # =&gt; AssertionError: assertion failed

# A Define debug_mode with an alias
# B Test @parameter if
# C The assert_true will only be included if debug_mode is True</code></code></pre><p>Only when the condition of the <code>if</code> is <code>True</code> will the code of that branch be included in the compiled binary. The <code>if</code> statement will never run during run time.</p><p>Even better, you could also use the <code>if_defined[]</code> technique to set the alias <code>debug_mode</code> to <code>True</code> only if the program was started with a command-line option <code>debug_mode</code>, like this:</p><pre><code><code>$ mojo -D debug_mode parameter_if_cmd.mojo</code></code></pre><p>Our alias is then defined with:</p><pre><code><code>from sys import is_defined
alias debug_mode = True if is_defined[&#8221;debug_mode&#8221;]() else False</code></code></pre><p>That way, you don&#8217;t have to change the program code, only a command-line option is needed to change the mode to debug. Start the program with:</p><pre><code><code>$ mojo parameter_if_cmd.mojo</code></code></pre><p>(or <code>./parameter_if_cmd</code>) and the assertion code will not be included or executed.</p><p>We saw how to use <code>@parameter</code> in combination with <code>if</code> and <code>for</code> to run the code it precedes at comptime. We can also use this decorator to make a function run at comptime.</p><h2>Running a function at comptime with <code>@parameter</code></h2><p>In the following example, the <code>add</code> function, which takes two integer parameters <code>a</code> and <code>b</code> and returns an integer, is executed at comptime because it is decorated with <code>@parameter</code> (see <code>parameter_decorator.mojo</code>):</p><pre><code><code>fn add_print[a: Int, b: Int]():
    @parameter                        # A
    fn add[a: Int, b: Int]() -&gt; Int:
        return a + b

    var x = add[a, b]()               # B
    print(x)

fn main():
    add_print[5, 10]()  # =&gt; 15
# A The add function is annotated with @parameter
# B The add function is called at comptime and its result is printed</code></code></pre><p>This translates at compile time to:</p><pre><code><code>fn add_print():
    var x = 15
    print(x)

fn main():
    add_print()</code></code></pre><p>The <code>add</code> calculation ran at compile time, so you pay no run-time price for anything inside the function.</p><p>Closures in Mojo can also handle parameters. Let&#8217;s first show what a closure is, and then discuss how they work with parameters.</p><h1>Closures</h1><p>A function takes values in as arguments when it is called and processes these; this is how a function gets its data. But there is another way that a function can get data from the surrounding scope, used with nested functions. This is often called capturing data, and the function is called a <strong>closure</strong>: it copies in (closes over) the variables to get its data. The captured variables are owned by the closure. The variables that are captured must be initialized before the definition of the function itself. Capturing means that the closure knows the values of any variables in context.</p><p>Listing 4 is an example of a closure <code>inner()</code>, which captures the value of the <code>num</code> variable (<a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/6_Using_Functions/closure.mojo">see </a><code>closure.mojo</code>):</p><blockquote><p><strong>Listing 4 &#8212; A closure capturing variables</strong></p></blockquote><pre><code><code>fn outer(f: fn () escaping -&gt; Int):   # A
    print(f())                        # B

fn call_it():
    var num = 5                       # C

    fn inner() -&gt; Int:                # D
        return num                    # E

    outer(inner)

fn main():
    call_it()  # =&gt; 5
# A The function type of inner is fn () escaping -&gt; Int
# B The closure inner is called here
# C The variable num will be captured
# D The function inner is a closure that captures the context variable num
# E num is known inside inner</code></code></pre><p>The value of <code>num</code> is copied to the <code>inner</code> function. This all happens at run time, and such a closure can be passed as an argument to other functions, like in our example. The <code>inner</code> closure is passed to the <code>outer</code> function. <code>inner()</code> is called a <strong>run-time closure</strong>, and such closures have as type: <code>fn() escaping &#8594; Type</code>. The <code>escaping</code> keyword indicates that this closure can &#8220;escape&#8221; the scope in which it was defined, meaning it can be returned from the function and used elsewhere.</p><p>An error message you can get when you call a function but forget the argument list <code>()</code>, is the following: <code>error: cannot emit closure for method &#8216;funcname&#8217;</code>, or: <code>function pointer was formed but not called, did you forget &#8216;()&#8217;s?</code> Without <code>()</code>, Mojo thinks you want to use the function as a closure. Simply add <code>()</code> to the back of <code>funcname</code> to solve this.</p><p>Compile-time closures do exist also, as we&#8217;ll discuss now. To declare them we need the decorator <code>@parameter</code>.</p><h2>Defining parametric closures with <code>@parameter</code></h2><p>Closures are local functions that can capture variables from their context, as we saw in the previous section. Now what if you would like to pass the inner function to the outer function, not as an argument, but as a parameter?</p><p>Then this code:</p><pre><code><code>var num = 5
fn inner() -&gt; Int:
    return num

outer[inner]()</code></code></pre><p>results in an</p><pre><code><code>error: cannot use a dynamic value in call parameter</code></code></pre><p>Why is this? The <code>inner</code> function is dynamic because it is still executed at run time and thus cannot be passed as a parameter. Decorating the <code>inner</code> function with <code>@parameter</code> makes it static, which means it is executed at comptime, so it can be passed as a parameter. Of course, we also need to change the function header of <code>outer</code>, adding <code>[]</code> and not forgetting the <code>()</code> in the definition as well as the call. The keyword <code>escaping</code> now also needs to change to <code>capturing</code>, which indicates that a function can access and use variables from its enclosing scope.</p><p>The complete example with <code>inner</code> used as a parameter goes as follows (<a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/6_Using_Functions/parametric_closure.mojo">see </a><code>parametric_closure.mojo</code>):</p><blockquote><p><strong>Listing 5 &#8212; Defining a parametric closure</strong></p></blockquote><pre><code><code>fn outer[f: fn () capturing -&gt; Int]():    # C
    print(f())

fn call_it():
    var num = 5

    @parameter                             # A
    fn inner() -&gt; Int:
        return num

    outer[inner]()                         # B

fn main():
    call_it()  # =&gt; 5
# A Decorator @parameter is needed to pass inner as a parameter
# B Passing inner as a parameter. Don&#8217;t forget the ()
# C The parameter declaration needs to declare the function parameter as capturing</code></code></pre><p>Compare this code with the previous listing. It is a good exercise to change the code step by step, going from the previous code to the parametric code.</p><p>Closures like <code>inner()</code> are called <strong>parametric closures</strong> or <strong>compile-time closures</strong>. They capture values defined before their declaration: <code>inner()</code> captures the value of <code>num</code>. Their function type is always of the form: <code>fn() capturing &#8594; Type</code>.</p><p>To make sure that the captured value(s) are copied into the closure (instead of passing a reference to the closure) precede <code>@parameter</code> with the decorator <code>@__copy_capture</code>, like this:</p><pre><code><code>@__copy_capture(num)
@parameter</code></code></pre><p>Parametric closures are the backbone of stdlib functions like <code>vectorize</code> and <code>parallelize</code>, which are heavily used in calculations.</p><h1>Structs with parameters</h1><p>Like functions, generic structs can be defined with parameters declared between <code>[]</code>. Many structs from the stdlib take a type as parameter. This is processed at comptime to generate optimal code for that type. The SIMD vector type, which was discussed in a previous article, is defined with two parameters as: <code>struct SIMD[type: DType, size: Int]</code>.</p><p>Here is an example:</p><pre><code><code>var sd = SIMD[DType.int16, 4](1, 2, 3, 4)</code></code></pre><p>You can also enhance your own structs with parameters, as done in the following listing (<a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/blob/main/7_Structs_and_Traits/planets_param.mojo">see </a><code>planets_param.mojo</code>):</p><blockquote><p><strong>Listing 6 &#8212; Using parameters in struct definition</strong></p></blockquote><pre><code><code>@fieldwise_init
struct Planet[earth_like: Bool]:             # A
    var mass: Int
    var name: String

    fn describe_planet(self):
        if earth_like:                       # B
            print(&#8221;Attention: Earth-like planet ahead!&#8221;)
        print(&#8221;Name:&#8221;, self.name, &#8220;Mass:&#8221;, self.mass)

fn main():
    var mars = Planet[True](80, &#8220;Mars&#8221;)      # C
    mars.describe_planet()  # =&gt;
    # Attention: Earth-like planet ahead!
    # Name: Mars Mass: 80
# A Definition of parameter earth_like
# B Parameters can be used in method&#8217;s code
# C A value is given to the parameter when the struct is made</code></code></pre><p>A parameter <code>earth_like</code>, which must be known at comptime, is defined for the struct <code>Planet</code>. Now when defining a new instance of <code>Planet</code>, you must give the parameter a value accordingly. This value can then be used in code, as we did here in the method <code>describe_planet</code>.</p><p>A general struct header schematically looks like this: <code>struct[parameters](arguments)</code>.</p><p>Another stdlib type that uses parameters is <code>List</code>, defined in the module <code>collections</code>. <code>List</code> is like a workhorse in Mojo, used for many common tasks.</p><p>Lists in Mojo are homogeneous, containing only one type of items. This type can be specified at comptime as a parameter like <code>List[Float64]</code>. That&#8217;s why such a type is generic: you can define it for all types, like a <code>List</code> can contain <code>Int16</code> values, or <code>Float64</code>, or <code>String</code>, or <code>struct Person</code> instances. The <code>Dict</code> type for a dictionary also takes key and value types as parameters. <code>List</code> and <code>Dict</code> are statically typed in Mojo; that is, the types of their data must be known at comptime.</p><p>New metaprogramming techniques will surely be added in the coming Mojo versions.</p><div><hr></div><p>&#169; 2025 Ivo Balbaert. All rights reserved.</p>]]></content:encoded></item><item><title><![CDATA[Building with Mojo (Part 3): Python and Mojo]]></title><description><![CDATA[Converting a Python program to Mojo, and exploring how they interact.]]></description><link>https://deepengineering.net/p/building-with-mojo-part-3-python</link><guid isPermaLink="false">https://deepengineering.net/p/building-with-mojo-part-3-python</guid><dc:creator><![CDATA[Ivo Balbaert]]></dc:creator><pubDate>Thu, 09 Oct 2025 07:48:01 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3f602cbb-615d-4272-91c3-2e88d5092315_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This article is Part 3 of our ongoing series on the Mojo programming language. <strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language">Part 1</a></strong> introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance. </em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;6a9d0448-b61a-49e6-b94b-aa8f466ec0af&quot;,&quot;caption&quot;:&quot;This article, kicks off a multi-part series exploring Mojo, a new programming language developed by the AI company Modular. Mojo is designed for AI-scale performance without abandoning Pythonic ergonomics. In Part 1, we&#8217;ll cover the following topics:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 1): A Language Born for AI and Systems&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-07-16T10:22:41.479Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e127b69e-ec68-43db-88a6-294f5ee6343d_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language&quot;,&quot;section_name&quot;:&quot;Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:168455891,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:1,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p><em><strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd">Part 2</a></strong> covered </em>Mojo&#8217;s SIMD-first model in practice.</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;a585458b-5f1f-4384-a18a-b572c390dadc&quot;,&quot;caption&quot;:&quot;This article is Part 2 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 2): Using SIMD in Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-09-11T08:28:42.258Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79fa439e-7d92-49ac-b0a2-2be7b06591cf_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd&quot;,&quot;section_name&quot;:&quot;Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:173251283,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></blockquote><p>Mojo is created with a distinct Python flavor to bring unification to AI development and deployment.</p><p>Most AI LLMs today use Python as their main language. This means that MAX, the AI engine framework from Modular written in Mojo, must be able to talk to and use Python. Indeed, MAX has a Python API to work with Python AI models, as well as a Mojo and C API.</p><p>This article covers:</p><ul><li><p>A comparison between Python and Mojo</p></li><li><p>How the integration between Mojo and Python works</p></li><li><p>Showing the advantages of Mojo calling out to Python</p></li><li><p>Working with useful Python functions and <code>PythonObject</code>s</p></li><li><p>Working with local Python modules</p></li><li><p>Applying functionality from modules in the Python ecosystem</p></li><li><p><strong>Calling Mojo from Python!!!!</strong></p></li></ul><p>Python can still do many things Mojo isn&#8217;t yet able to do, such as working with classes or operating on big integers. In the 35+ years of its existence, people have built a gigantic infrastructure of thousands of modules&#8212;from numerical computation (NumPy, Matplotlib), data science (Pandas), and AI (PyTorch, TensorFlow) to graphics, gaming, and beyond. Although Mojo equivalents are being built right now, it will take time to make them as battle-tested and functionally complete as their Python counterparts. So, for the foreseeable future, Mojo and MAX will need to work smoothly with Python.</p><p>Mojo is a member of the Python family of languages. It&#8217;s no surprise, then, that as a developer, you&#8217;ll need basic Python skills. More important is learning to read the documentation of Python libraries to understand the API required to talk to Python&#8212;calling Python functions from Mojo. Using existing Python functionality will help you, as a Mojo developer, build a complete working project faster, enriching your portfolio or that of your company.</p><p>In this article, you&#8217;ll learn how to do just that. Afterwards, the Python parts can be slowly migrated to Mojo, further enhancing your project&#8217;s performance.</p><p>To follow along, first follow the instructions <a href="https://docs.modular.com/max/get-started">here</a> to install pixi. Then use pixi to create a project called <code>python_integration</code> and <code>cd</code> into it. We&#8217;re going to let Mojo work together with Python, so we need a running Python environment inside the Mojo project. Add a recent Python version such as 3.12 to the project with the command:</p><pre><code><code>$ pixi add &#8220;python==3.12&#8221;</code></code></pre><p>and start a magic shell.</p><p>To add the necessary Python libraries, run:</p><pre><code><code>$ pixi add numpy matplotlib</code></code></pre><p>All code examples used in this chapter can be found in the code repo <a href="https://github.com/Ivo-Balbaert/Mojo_in_Action/tree/main/5_Python_Integration">here</a>.</p><p>The code in this article uses Python integration and calculations at compile time. We&#8217;ll discuss these topics more in depth in later articles. All code has been tested in Mojo&#8217;s latest stable version 25.6.</p><div><hr></div><h1>A Comparison between Python and Mojo</h1><p>We&#8217;ll start by converting a Python program to its Mojo equivalent step by step. The following section compares the same algorithm implemented in both Python and Mojo, and we compare the performance of both.</p><h2>Compiling and executing a simple Mojo program</h2><p>As a start, let&#8217;s do a Hello World! for the whole planet in Python (see <code>hello_world_uni.py</code>):</p><pre><code><code>print(&#8221;Hello World from Python!&#8221;)
print(&#8221;Bonjour tout le monde!&#8221;)
print(&#8221;Hallo Wereld!&#8221;)
print(&#8221;&#161;Hola, mundo!&#8221;)
print(&#8221;&#922;&#945;&#955;&#951;&#956;&#941;&#961;&#945; &#954;&#972;&#963;&#956;&#949;!&#8221;)
print(&#8221;&#20320;&#22909;&#8221;)
print(&#8221;&#12371;&#12435;&#12395;&#12385;&#12399; &#19990;&#30028;!&#8221;)
print(&#8221;&#3754;&#3760;&#8203;&#3738;&#3762;&#3725;&#8203;&#3732;&#3765;&#8203;&#3722;&#3762;&#3751;&#8203;&#3778;&#3749;&#3713;&#8221;)
print(&#8221;&#128075;&#127757;&#10071;&#8221;)</code></code></pre><p>Run it with:</p><pre><code><code>$ python3 hello_world_uni.py</code></code></pre><p>Output:</p><pre><code><code>Hello World from Python!
Bonjour tout le monde!
Hallo Wereld!
&#161;Hola, mundo!
&#922;&#945;&#955;&#951;&#956;&#941;&#961;&#945; &#954;&#972;&#963;&#956;&#949;!
&#20320;&#22909;
&#12371;&#12435;&#12395;&#12385;&#12399; &#19990;&#30028;!
&#3754;&#3760;&#8203;&#3738;&#3762;&#3725;&#8203;&#3732;&#3765;&#8203;&#3722;&#3762;&#3751;&#8203;&#3778;&#3749;&#3713;
&#128075;&#127757;&#10071;</code></code></pre><p>Now write the Mojo equivalent (see <code>hello_world_uni.mojo</code>):</p><blockquote><p><strong>Listing 3.1 &#8211; A Unicode Hello World! program</strong></p></blockquote><pre><code><code>def main():
    print(&#8221;Hello World from Mojo!&#8221;)
    print(&#8221;Bonjour tout le monde!&#8221;)
    print(&#8221;Hallo Wereld!&#8221;)
    print(&#8221;&#161;Hola, mundo!&#8221;)
    print(&#8221;&#922;&#945;&#955;&#951;&#956;&#941;&#961;&#945; &#954;&#972;&#963;&#956;&#949;!&#8221;)
    print(&#8221;&#20320;&#22909;&#8221;)
    print(&#8221;&#12371;&#12435;&#12395;&#12385;&#12399; &#19990;&#30028;!&#8221;)
    print(&#8221;&#3754;&#3760;&#8203;&#3738;&#3762;&#3725;&#8203;&#3732;&#3765;&#8203;&#3722;&#3762;&#3751;&#8203;&#3778;&#3749;&#3713;&#8221;)
    print(&#8221;&#128075;&#127757;&#10071;&#8221;)</code></code></pre><p>For a change, let&#8217;s run it on the command line, outside of VS Code:</p><pre><code><code>$ mojo hello_world_uni.mojo</code></code></pre><p>(This is short for: <code>mojo run hello_world_uni.mojo</code>.)</p><p>As expected, this displays the same &#8220;Hello World&#8221; messages as Python.</p><p>We see that Mojo is as friendly as Python and supports Unicode. You&#8217;ll notice that in Mojo all statements are enclosed in a block preceded by <code>def main()</code>:</p><p>In Python you can write top-level code, but in Mojo this is not yet possible (except in the REPL or in a Jupyter notebook): all code must be enclosed in a function.</p><p>Moreover, the top-level code must be written in a <code>main()</code> function with no arguments and no return type. <code>main()</code> is automatically called as the <em>starting point of execution</em> in Mojo. It also envelops the complete program execution from start to end.</p><p>Looking at the Mojo version, you&#8217;ll see quite a lot of Python features:</p><ul><li><p><code>def</code> to declare a function</p></li><li><p>The <code>:</code> character to end a function or control statement, marking the start of an indented code block</p></li><li><p>Significant whitespace and indentation</p></li></ul><p><code>print()</code> is a function that displays its argument to the terminal, including a newline.</p><blockquote><p><strong>Experiment:</strong></p><p>Experiment with removing or adding spaces before the <code>print</code> statements and see what the compiler thinks about this. &#8220;Statement has excess indentation&#8221; will remind you when the indentation is wrong. By convention, Mojo uses four spaces per indentation level. Each level starts a new code block. Make sure your code editor uses spaces only and converts tabs to spaces.</p></blockquote><p>The <code>mojo</code> command compiles to machine code and executes that in memory&#8212;it doesn&#8217;t produce an executable file.</p><h2>Building the code into an executable</h2><p>A typical production environment doesn&#8217;t have MAX or Mojo installed. This isn&#8217;t necessary, because a Mojo project compiles to a single executable file with the <code>mojo build</code> command.</p><p>This simplifies deployment tremendously: you just copy the executable to the target production machine and run it. Let&#8217;s try this:</p><pre><code><code>$ mojo build hello_world_uni.mojo</code></code></pre><p>Now a binary file <code>hello_world_uni</code> is produced, which you can execute as <code>./hello_world_uni</code>, displaying the same output as before.</p><p>Go ahead, do it. Copy it elsewhere on your machine and see that it runs.</p><blockquote><p><strong>Note:</strong> In development, the simple <code>mojo</code> command to run a program is just fine. You&#8217;ll need a build only when deploying into a test or production environment. It&#8217;s also important for benchmarking: an executable usually runs at higher speed because no JIT compilation is needed.</p></blockquote><div><hr></div><h2>Converting Python programs to Mojo</h2><p>In this section, we&#8217;ll see that there isn&#8217;t much difference between a simple Python program and its Mojo equivalent. Then we&#8217;ll look at a non-trivial Python program and convert it step by step to Mojo.</p><h3>A simple example</h3><p>Here is a Python program to add two integers (see <code>adding.py</code>):</p><pre><code><code>def add(x, y):
    return x + y

z = add(3, 5)
print(z) # =&gt; 8</code></code></pre><p>which we could rewrite as (see <code>adding2.py</code>):</p><pre><code><code>def add(x, y):
    return x + y

def main():
    z = add(3, 5)
    print(z) # =&gt; 8

main()</code></code></pre><p>Running both programs yields 8.</p><p>Now make a copy of this program and rename it to <code>adding2.mojo</code>. When compiling in VS Code, you&#8217;ll see the following error:</p><blockquote><p><code>error: TODO: expressions are not yet supported at the file scope level</code></p></blockquote><p>We get rid of this by removing the call to <code>main()</code>, which isn&#8217;t needed in Mojo. Then, in <code>def add(x, y):</code>, we see another error:</p><blockquote><p><code>error: argument type must be specified</code></p></blockquote><p>That&#8217;s right&#8212;even in a <code>def</code> function, the arguments and the return value must be typed. Because the values are integers, they have the type <code>Int</code>.</p><p>The resulting program is valid Mojo code:</p><pre><code><code>def add(x: Int, y: Int) -&gt; Int:
    return x + y

def main():
    z = add(3, 5)
    print(z) # =&gt; 8</code></code></pre><p>Note that when running this code, Mojo does not use CPython or any Python interpreter. Mojo also has a more robust style using <code>fn</code> as the keyword for a function instead of <code>def</code>. In <code>fn</code> mode, it is mandatory to add a type to every function argument, and the function must be annotated with <code>raises</code> if an error can occur inside the function.</p><p>Why is argument typing important? First, with type info, the compiler can check whether the arguments passed to the function have the correct type. The wrong type will give a compile error, rather than crashing your program at runtime as Python can. The code becomes more robust; you don&#8217;t even have to run it to catch certain errors. Second, since the compiler knows the exact types, it can generate machine code specifically for those types. For example, code for integers will differ from code for floating-point numbers. Doing so, the machine code can be highly optimized, leading to a big performance increase.</p><p>Let&#8217;s make this change in the code (see <code>adding.mojo</code>):</p><pre><code><code>fn add(x: Int, y: Int) -&gt; Int:    #A
    return x + y

fn main():
    z = add(3, 5)
    print(z)  # =&gt; 8

#A Adding argument types for robustness and performance</code></code></pre><h3><code>def</code> and <code>fn</code></h3><p>Now you might ask: what&#8217;s with <code>def</code> versus <code>fn</code>, and how do I choose which to use?</p><ul><li><p><code>def</code> is as if you said to the compiler, &#8220;I just want to code&#8212;stop bothering me; speed is not my concern.&#8221; It&#8217;s more Pythonic. Mojo assumes any <code>def</code> function can raise an error. Functions are dynamic and types are optional for local variables. <code>def</code> is great for high-level programming and scripting but less suited to systems-level programming. Yes, you can use types with <code>def</code> when declaring variables with <code>var</code>, and this is mandatory for function arguments. But then, why not use an <code>fn</code> function?</p></li><li><p><code>fn</code> is as if you said to the compiler, &#8220;Let&#8217;s get fast&#8212;and show me everything that could go wrong.&#8221; It&#8217;s the Rust way: statically typed and aiming for higher performance. <code>fn</code> enforces strongly typed code and guarantees memory-safe behavior.</p></li></ul><p>If the compiler thinks there is any chance of an error being raised inside the function, the function must be annotated explicitly with <code>raises</code> to indicate this.</p><p>Declaring the type with <code>var</code> is not required for variables inside an <code>fn</code>, but it&#8217;s considered best practice: with type info, the compiler can generate faster, safer code.</p><p>Both <code>def</code> and <code>fn</code> warn you when a variable is unused, to guard against misspellings or superfluous variables.</p><blockquote><p><strong>Experiment:</strong><br>In the typed Mojo program <code>adding.mojo</code>, try to add two decimal numbers. Does it work? What message do you get? Do you get the message at compile time or when running the program?</p></blockquote><h3>An algorithmic example: binary search</h3><p>Here we start with a non-trivial Python program and convert it step by step to Mojo (see <code>search.py</code>). What does this program do?</p><p>The code contains a binary search function that takes a sorted array <code>arr</code> and an element <code>x</code>. It searches whether <code>x</code> is present in this array, returning its index; otherwise it returns &#8722;1.</p><blockquote><p><strong>Note:</strong> The binary-search algorithm used here is widely known. If you need a refresher, see <a href="https://www.geeksforgeeks.org/dsa/binary-search/">read this</a>. </p></blockquote><blockquote><p><strong>Listing 3.2 &#8211; A binary search Python program</strong></p></blockquote><pre><code><code>def binary_search(arr, x):
    low = 0
    high = len(arr) - 1
  
    while low &lt;= high:
        mid = (high + low) // 2

        if arr[mid] &lt; x:
            low = mid + 1
        elif arr[mid] &gt; x:
            high = mid - 1
        else:
            return mid

    return -1

# top-level code:
n = 1_000_000         #A
arr = []
for i in range(n):
    arr.append(i)     #B           
results = []                
for i in range(n):    #C          
    results.append(binary_search(arr, n - i))
print(&#8221;Results: &#8220;, len(results))

# =&gt; Results:  1000000

#A The _ can be used in numbers to make it easier to read 
#B First loop: populate array arr
#C Second loop: call binary search 1,000,000 times
</code></code></pre><p>The main code sets up the data structures. The first loop appends all numbers from 0 to 999,999 to the array. Then, in a second loop over the 1,000,000 numbers, it performs a binary search on each integer <code>n &#8211; i</code>, which must be present in the array. The <code>//</code> operator inside the function means floor division (for example, <code>5 // 2</code> gives <code>2</code>). At the end, we display the length of the <code>results</code> array to verify we have found all numbers.</p><p>Let&#8217;s now convert this program to Mojo. First make a copy of <code>search.py</code> and rename it to <code>search.mojo</code>.</p><p>Immediately, the editor fills with (luckily all the same) errors:</p><blockquote><p><code>TODO: expressions are not yet supported at the file scope level.</code></p></blockquote><blockquote><p><strong>Tip:</strong> Put your cursor on the error line, then use F8 to enlarge the message or copy it.</p></blockquote><p>We know how to handle this: just place a <code>def main():</code> before the top-level code and indent the code.</p><p>The next error we see is:</p><blockquote><p><code>cannot emit an empty list without a contextual type</code></p></blockquote><p>for the <code>arr</code> and <code>results</code> variables. Like in Python, we want them to be dynamic lists.</p><p>The closest to this in Mojo is <code>List</code>, which is defined in the module <code>collections</code> but is automatically known.</p><p>Instead of declaring <code>arr</code> as an <code>[]</code>, we must declare it with its <code>List</code> type: <code>arr = List[Int32]()</code>, and do the same for <code>results</code>. They are both a <code>List</code> of 32-bit integers. The <code>()</code> at the end is a call to the <code>List</code> constructor. Basically, we have given the two variables a type, which makes the code more error-proof and speeds up the execution.</p><p>We still have an error on the <code>def binary_search</code>:</p><blockquote><p><code>argument type must be specific</code></p></blockquote><p>The arguments must be typed in both <code>fn</code> and <code>def</code> functions. Let&#8217;s fill in the concrete type: <code>def binary_search(arr: List[Int32], x):</code>.</p><p>While we&#8217;re at it, let&#8217;s type the search argument <code>x</code> as a 32-bit integer as well:<br><code>def binary_search(arr: List[Int32], x: Int32):</code>.</p><p>Now we see another error on the return lines:</p><blockquote><p>cannot implicitly convert &#8216;Int&#8217; value to &#8216;None&#8217; in return value</p></blockquote><p>The result of <code>binary_search</code>, which is the position of <code>x</code> in the array <code>arr</code>, is not indicated. This cannot be converted to an <code>Int32</code>, which is the element type of <code>results</code> and exactly what <code>append</code> expects.</p><p>Let&#8217;s specify the return type with <code>-&gt; Int32</code>:<br><code>def binary_search(arr: List[Int32], x: Int32) -&gt; Int32:</code></p><p>(We get a warning on the line <code>mid = 0</code>; this line can be deleted because <code>mid</code> is initialized in the <code>while</code> loop.)</p><p>Finally, our Mojo code compiles and displays the same output as the Python code. Note that the code block of binary search is exactly the same in the Python and the Mojo version.</p><p>Since we clearly moved our code to a stricter version of Mojo with types, change <code>def</code> to <code>fn</code> for the two functions. The resulting code is shown below (see <code>search.mojo</code>).</p><blockquote><p><strong>Listing 3.3 &#8211; A binary search Mojo program</strong></p></blockquote><pre><code><code>fn binary_search(arr: List[Int32], x: Int32) -&gt; Int32:    #B
    low = 0
    high = len(arr) - 1

    while low &lt;= high:
        mid = (high + low) // 2

        if arr[mid] &lt; x:
            low = mid + 1
        elif arr[mid] &gt; x:
            high = mid - 1
        else:
            return mid

    return -1

fn main():
    n = 1_000_000
    arr = List[Int32]()        #C
    for i in range(n):
        arr.append(i)
    results = List[Int32]()    #C

    for i in range(n):
        results.append(binary_search(arr, n - i))
    print(&#8221;Results: &#8220;, len(results))

# =&gt; Results:  1000000

#B Typed binary search function
#C Typing arr and results</code></code></pre><p>With very little effort (changing three lines), we converted the Python code to Mojo. Let&#8217;s now see how they perform.</p><h2>Comparing the speed of Python and Mojo in a simple algorithm</h2><p>In the previous section, we used a binary-search algorithm and executed 1,000,000 calls to that function. We wrote both a Python and a Mojo version. Let&#8217;s now time the difference.</p><p>This is very simple:</p><ul><li><p>Capture the time before the loop that calls binary search (call it <code>start</code>) and after that loop (<code>end</code>). So <code>(end - start)</code> is the time difference.</p></li><li><p>To work with time in Python, import it: <code>from time import time</code>.</p></li><li><p>The time is returned in seconds; to get milliseconds (ms) multiply by 1000.</p></li><li><p>To work with time in Mojo, use the <code>perf_counter_ns()</code> function and import it from the module <code>time</code>: <code>from time import perf_counter_ns</code>.</p></li><li><p>The time there is returned in nanoseconds; to get ms divide by 1,000,000.</p></li></ul><p>We are not making a real benchmark here; we just want an idea of the difference between the two versions.</p><p>Comparing Python and Mojo, we show the Python version in Listing 3.4 (see <code>search_timed.py</code>; we leave out code common with Listing 3.2):</p><blockquote><p><strong>Listing 3.4 &#8211; Timed Python binary search</strong></p></blockquote><pre><code><code>from time import time

def binary_search(arr, x):
                            #A

# top-level code:
                            #A
start = time()
for i in range(n):
    results.append(binary_search(arr, n - i))
end = time()
print((end - start) * 1000, &#8220;ms&#8221;)

# =&gt; 1290.342092514038 ms

#A Same code as in Listing 3.2
</code></code></pre><p>An average of 10 runs gives 1269 ms on my machine.</p><p>In the Mojo version, the variables and the <code>binary_search</code> function are typed.</p><blockquote><p><strong>Listing 3.5 &#8211; Timed Mojo binary search</strong></p></blockquote><pre><code><code>from time import perf_counter_ns     #A
from collections import List         #A

fn binary_search(arr: List[Int32], x: Int32) -&gt; Int32:
                          #B
fn main():
                          #B
    start = perf_counter_ns()
    for i in range(n):
        results.append(binary_search(arr, n - i))
    end = perf_counter_ns()
    print((end - start) / 1e6, &#8220;ms&#8221;)

# =&gt; 56.476036999999998 ms

#A Import perf_counter-ns and List from their modules time and collections
#B Same code as in Listing 3.3</code></code></pre><p>An average of 10 runs gives 57 ms, which is 22.3&#215; faster than the Python version! The algorithm and loops remain the same, and the code still looks very Pythonic.</p><div><hr></div><h1>Why use Python together with Mojo?</h1><p>Mojo is a young language and currently doesn&#8217;t have many battle-tested libraries outside of <code>stdlib</code>. Python, by contrast, is decades old and has a vast ecosystem of well-tested libraries. It&#8217;s evident that Mojo projects would want to use these modules. Also, the gigantic amount of Python code that exists as modules in applications must be reusable in new Mojo projects.</p><p>As time goes by, Mojo equivalents for these Python libraries will appear and replace them. In the meantime, use Python for what it&#8217;s good at&#8212;for example, graph plotting or AI modeling with PyTorch&#8212;and for things that do not yet exist or are more difficult to rewrite in Mojo.</p><p>Why can you be confident that the Mojo ecosystem will grow rapidly? Because Mojo, being compiled, delivers far better performance (in the range 10&#8211;10000, and when fine-tuned even more) than Python, which is interpreted. With what we now know about Mojo, we can answer the following question in more depth.</p><h3>Why is Mojo more performant?</h3><blockquote><ul><li><p>With Mojo, there is no overhead associated with compiling to bytecode and running through an interpreter as Python does. Mojo compiles to native code, which runs inherently much faster.</p></li><li><p>Python works with references (a kind of pointer) for all variables, even for simple numbers. Mojo, on the other hand, always uses SIMD for numbers and math operations. Numbers (and other simple values) sit on the stack and can be passed straight into SIMD registers to be processed in bulk. We don&#8217;t need to look up these values on the heap via an address as in Python.</p></li><li><p>All the expensive allocations on the heap, garbage collection, and indirections (looking up values through references) that Python does are not required in Mojo.</p></li><li><p>The Mojo compiler can do huge optimizations because it knows the types of its variables. For example, knowing which numeric type is used in a piece of Mojo code can lead to highly optimized executable code. Because Python doesn&#8217;t know the type of its variables, it can&#8217;t do AOT (ahead-of-time) optimizations.</p></li></ul></blockquote><div><hr></div><h2>Two-way communication</h2><p>Python&#8211;Mojo two-way communication is needed to get Python developers on board and to gradually migrate Python projects to Mojo.</p><p>What currently works very well is Mojo calling out to Python and getting results back. The other way around&#8212;Python calling into (much more performant) Mojo code&#8212;is also possible, but a bit more involved at the moment.</p><h2>Conversion tool from Python to Mojo</h2><p>An automated Python-to-Mojo code translation tool would be very helpful and will be provided by Modular. An open-source attempt, <code>py2mojo</code> by Manuel Saelices, can be found <a href="https://github.com/msaelices/py2mojo">here</a>.</p><p>Mojo&#8217;s long-term goal is to become a superset of Python. Then it will no longer use CPython. No intermediary solution will be necessary because Mojo&#8217;s compiler will process all code as Mojo code, including when it is written as pure Python.</p><div><hr></div><h1>Mojo calls Python &#8211; A simple example</h1><p>If you are a Python developer, you probably have many favorite Python libraries and a bunch of self-written Python code for your projects. You don&#8217;t want to throw that away&#8212;you want to use that code from Mojo. And of course, you want to use modules across the Python ecosystem as well. We&#8217;ll show you how to do just that, starting with a simple example and later using well-known Python libraries, all with the same simple technique.</p><p>Suppose we have our own Python module <code>compute.py</code> containing some functions for calculations. To keep it simple, let&#8217;s start with multiplication and exponentiation:</p><pre><code><code>def mul(n, m):
    return n * m

def pow(n, m):
    return n ** m</code></code></pre><p>Let&#8217;s add this functionality to our Mojo program by importing the Python module. Python inside Mojo is a different execution environment, so Mojo braces against possible errors occurring in Python by using the <code>raises</code> keyword (see <code>main.mojo</code>):</p><blockquote><p><strong>Listing 3.6 &#8211; Mojo using a local Python module</strong></p></blockquote><pre><code><code>from python import Python       #A

fn main() raises:               #E
    var i: Int = 42
    var j: Int = 7

    Python.add_to_path(&#8221;.&#8221;)      #B
    var calc = Python.import_module(&#8221;compute&#8221;)    #C

    var res1 = calc.mul(i, j)    #D
    var res2 = calc.pow(i, j)
    print(res1, res2)  # =&gt; 294 230539333248

    var np = Python.import_module(&#8221;numpy&#8221;)        #C
    var arr = np.arange(7)&#9;&#9;                #D&#9;
    print(arr)  # =&gt; [0 1 2 3 4 5 6]

#A Import the python module. 
#B Adds a directory to the Python path.
#C Import the specific Python module to use.
#D Call the Python functions.
#E Calling Python from Mojo always needs raises.
</code></code></pre><p>The <code>python</code> module (part of Mojo&#8217;s <code>stdlib</code>) is all that is needed for Python integration. The <code>add_to_path()</code> function tells Python where it can find the Python module to be used. <code>compute.py</code> lives in the current directory, which we include with <code>&#8220;.&#8221;</code>. If the Python module is stored elsewhere in your filesystem, change this to <code>Python.add_to_path(&#8221;/path/to/module&#8221;)</code>.</p><blockquote><p><strong>Experiment: Using a module from another folder</strong><br>Now your <code>compute.py</code> is stored in a subfolder <code>calculations</code>, which is itself in a subfolder <code>my_python</code> under the location of <code>main.mojo</code>. Change <code>add_to_path</code> so that it works.</p></blockquote><p><code>Python.import_module()</code> does what it says (note that the <code>.py</code> extension is not needed). Just give it a name, then call the Python functions you need with the familiar dot notation, like <code>calc.mul(i, j)</code> or <code>np.arange(7)</code>. Continue with their results in the Mojo code.</p><p>The <code>Python</code> struct imported and used in the code is only a collection of static methods (like <code>add_to_path</code> and <code>import_module</code>) that enable you to use Python code in Mojo. They are called static because they are called on the <code>Python</code> struct itself, like <code>Python.import_module()</code> (see Chapter 6).</p><blockquote><p><strong>Note</strong><br>Code that uses Python in a Jupyter notebook must be preceded with <code>%%python</code>:</p><pre><code><code>%%python
print(&#8221;The answer is&#8221;, 42)&#9;# =&gt; 42</code></code></pre></blockquote><p>Let&#8217;s explore a little more in depth how this all works. CPython is the standard Python interpreter, and the same interpreter is used by Mojo to process Python code. This ensures that Python works exactly the same as it does when running stand-alone.</p><p>When processing Python code in Mojo, CPython is called at compile time and communicates with the Mojo compiler. This means that all Python processing happens at comptime. Mojo knows only one type for communication with Python, called <code>PythonObject</code>, which is used to contain all Python data.</p><blockquote><p><strong>Tip:</strong> A Mojo executable that uses Python does <strong>not</strong> contain the Python environment. So, you must ensure the production environment has Python installed&#8212;for example, as we did with:<br><code>$ pixi add &#8220;python==3.12&#8221;</code>.</p></blockquote><p>Also, every Python module used must be separately installed in that environment. Pixi makes this easy. If a required Python module is not present, the Mojo program will crash. For example, if NumPy is needed, install it with:<br><code>$ pixi add numpy</code>.<br>To add modules not available through the conda repo, you can also access PyPI through a command like:<br><code>$ pixi add --pypi &#8220;fastapi&#8221;</code>, which adds the <code>fastapi</code> library.</p><blockquote><p><strong>Note:</strong> When you work with Python in a Mojo project, a folder <code>__pycache__</code> is created, which contains <code>.pyc</code> files created by CPython (in our example a file <code>compute.cpython-312.pyc</code>). The next time the Python code is called, the bytecode compilation can be skipped.</p></blockquote><div><hr></div><h1>Python calls Mojo</h1><p>As said, this can already be used, but because this is a beta feature many changes will take place before it reaches maturity. You can find out more <a href="https://docs.modular.com/mojo/manual/python/mojo-from-python/">here</a>.</p><div><hr></div><h1>Using functions from the <code>python</code> module</h1><p>The following listing shows how to use the crucial <code>import_module</code> function, as well as the complementary <code>add_to_path</code> function. But the <code>Python</code> struct contains other useful functions, like <code>eval</code> and <code>evaluate</code> (see <code>python_functions.mojo</code>):</p><blockquote><p><strong>Listing 3.7 &#8211; Useful python functions</strong></p></blockquote><pre><code><code>from python import Python
from memory import UnsafePointer

fn main() raises:
    var py = Python()            #A

    var x = py.eval(&#8221;if 108 &gt;= 42: print(&#8217;ok&#8217;)&#8221;)  # =&gt; ok
    print(x)  # =&gt; True

    var t = py.eval(&#8221;108 &lt; 42&#8221;)    #B
    print(t)  # =&gt; True

    print(py.eval(&#8221;108 &gt;&lt; 42&#8221;))
    # =&gt;
    #   File &#8220;&lt;string&gt;&#8221;, line 1
    # 108 &gt;&lt; 42
    #      ^
    # SyntaxError: invalid syntax
    # False

    var y = py.evaluate(&#8221;42 * 7&#8221;)   #C
    print(y)  # =&gt; 294
    var pystr = py.evaluate(&#8221;&#8217;This string is built&#8217; + &#8216; inside Python&#8217;&#8221;)
    print(pystr)  # =&gt; This string is built inside Python
    var len_py = Python.evaluate(&#8221;len&#8221;)   #D
    print(len_py(&#8221;ABCDEFG&#8221;))  # =&gt; 7

    var w: Int = 42
    var pybt = Python.import_module(&#8221;builtins&#8221;)  #E
    pybt.print(&#8221;This uses the Python print function&#8221;)
    # =&gt; This uses the Python print function
    pybt.print(&#8221;The answer is&#8221;, w)  #F
    # =&gt; The answer is 42
    pybt.print(pybt.type(w))  # =&gt; &lt;class &#8216;int&#8217;&gt;    #G
    pybt.print(pybt.id(w))  # =&gt; 139804414609064    #G
    print(UnsafePointer(to=w))  # =&gt; 0x7ffcda444ea0

    var foods = Python.list()    #H
    foods.append(&#8221;carrot&#8221;)
    foods.append(&#8221;chicken&#8221;)
    print(foods)  # =&gt; [&#8217;carrot&#8217;, &#8216;chicken&#8217;]
    print(Python.type(foods))  # =&gt; &lt;class &#8216;list&#8217;&gt;

#A Make a shorthand for Python()
#B Using eval to check and run Python code, returns a Bool
#C Using evaluate to execute Python code, and return result
#D Taking the Python len function into Mojo
#E Import Python builtins to use its functions
#F Python can use Mojo&#8217;s variables
#G Using Python&#8217;s print, type and id functions
#H Creating a Python list</code></code></pre><p><code>Python()</code> creates an object of the <code>Python</code> struct (see Chapter 7), so we can give it a shorter name (here <code>py</code>) and call the various functions on it.</p><p>The <code>eval</code> function accepts a (reference to a) <code>String</code> and executes it if the string is valid Python code, returning <code>True</code> (even if the expression evaluated is false). If the code is not valid or raises an exception, it returns <code>False</code>.</p><p>The <code>evaluate</code> function also executes the first <code>String</code> argument as Python code, but it returns the result of the evaluation as a <code>PythonObject</code>, to be used in Mojo. You can even use it to access a built-in Python function like <code>len</code>, store it in a Mojo variable, and call it in Mojo.</p><p>Another way to do the same thing is to import the Python <code>builtins</code> module, give it a name, and start calling functions from this module, as in <code>pybt.type</code> (which gives you the dynamic type) and <code>pybt.print()</code>.</p><p>Python can also use a Mojo variable (in this example <code>w</code>). From the addresses (<code>id</code> in Python, <code>address_of</code> in Mojo) we can see that Python takes a copy of the variable. That copy is of type <code>Int</code> (or a <code>PythonObject</code> constructed from an <code>Int</code>) for Mojo, and <code>&lt;class &#8216;int&#8217;&gt;</code> for Python.</p><p>The <code>list()</code> and <code>dict()</code> functions construct an empty Python list and dict, and you use pure Python to work with them. For Mojo, these variables remain of type <code>PythonObject</code>.</p><div><hr></div><h1>Working with <code>PythonObject</code></h1><p>We already stated that all interaction between the Python interpreter and the Mojo compiler happens at comptime. A single, versatile type called <code>PythonObject</code> forms the bridge between Python and Mojo:</p><ul><li><p>All Python objects are viewed by Mojo as instances of <code>PythonObject</code> (an instance is a concrete version of a struct or class).</p></li><li><p>A <code>PythonObject</code> can be constructed from a wide variety of Mojo types (<code>SIMD</code> types, <code>String</code>, <code>StringRef</code>, <code>StringLiteral</code>, <code>Dict</code>, and so on) and can be used with functions such as <code>str</code>, <code>int</code>, <code>len</code>, and so forth.</p></li></ul><p>Moreover, <code>PythonObject</code> is register-passable, meaning the value can pass as a whole into SIMD registers, so it can be processed quickly. The <code>import_module</code> function also returns a <code>PythonObject</code>, which is a reference to the imported Python module. We give it a name (e.g., <code>calc</code>), and you can call all module functions on that object, like <code>calc.mul(i, j)</code>. The types of the results of these calls are then inferred by the Mojo compiler and further processed in Mojo.</p><p>Nearly every operation involving a <code>PythonObject</code> may raise a Python exception, so functions that use it must be marked <code>raises</code> and handle errors.</p><p><code>PythonObject</code> can be used in powerful ways, as the following examples demonstrate (see <code>python_object.mojo</code>):</p><ul><li><p>We can use all Python types in Mojo just by giving them an alias name, as &#167;5.4.1 shows where we use Python exponentiation <code>**</code>, or &#167;5.4.2 where we call the Python <code>hex()</code> method on its float type.</p></li><li><p>Another way to use Python types is by using the <code>Python.evaluate()</code> function, as illustrated in &#167;5.4.3. This can be used to construct a <code>PythonObject</code> from a literal expression, like <code>Python.evaluate(&#8221;[42, &#8216;cat&#8217;, 3.14159]&#8221;)</code>, which acts like a Python list object.</p></li></ul><p>This is useful when Python has functionality that Mojo doesn&#8217;t (yet) have. Rather than writing it in Mojo yourself, you can quickly use the Python version to accomplish your goal. For example, at the moment Mojo doesn&#8217;t support lists (or dictionaries) with different item types, so you could use a Python list that can contain heterogeneous types. In the following example we create a Python list object <code>py_list</code> and dynamically add items of different types:</p><pre><code><code>from python import Python

def main():
    py_list = Python.list()     #A
    py_list.append(7)           #B
    py_list.append(&#8221;forty_two&#8221;)
    py_list.append(2.718)
    py_list.append(True)
    print(py_list)  # =&gt; [7, &#8216;forty_two&#8217;, 2.718, True]  #C
#A Create a Python List
#B Append different type elements</code></code></pre><blockquote><p><strong>Tip</strong> Beware that <code>PythonObject</code>s are stored on the heap and must be garbage-collected; use them only when you can&#8217;t write pure Mojo code.</p></blockquote><p>You can initialize a <code>PythonObject</code> in several ways:</p><pre><code><code>from python import Python, PythonObject
alias py = PythonObject

var py: PythonObject = []    #A
var py = Python()  
var py: PythonObject = {}    #B    

#A Creates an empty Python list; you can append items and print the list as in the previous snippet
#B Creates an empty Python dictionary</code></code></pre><p>Or we can extract Python types to use them in our Mojo code like this:</p><pre><code><code>alias int = PythonObject
alias str = PythonObject
alias float = PythonObject</code></code></pre><h2>How to work with big integers in Mojo</h2><p>Using Python&#8217;s <code>int</code> as <code>PythonObject</code>, we can benefit from arbitrary-precision integers:</p><pre><code><code>    var x: int = 2 #A
    print(x**100)  # =&gt; 1267650600228229401496703205376
    var y: Int = 2 #B 
    print(y**100)  # =&gt; 0

#A x is a Python int, giving arbitrary precision in operations
#B y is a Mojo Int, with limited precision, and overflow without warning</code></code></pre><p>Python gives us the right answer here, while <code>Mojo Int</code> does not because it overflows.</p><blockquote><p><strong>Note:</strong> The community package <code>decimojo</code> has a <code>BigInt</code> type, which works like <code>int</code> in Python.</p></blockquote><h2>Using <code>PythonObject</code> for floats and strings</h2><p>In the following snippet, <code>f</code> and <code>s1</code> are Python objects of type <code>float</code> and <code>str</code>, showing the use of Python functions <code>hex</code> and <code>upper</code>:</p><pre><code><code>    var f: float = 0.6
    print(f.hex())  # =&gt; 0x1.3333333333333p-1
    var s1: str = &#8220;xxbaaa&#8221;
    print(s1.upper())  # =&gt; XXBAAA
    var s2: String = String(s1)
    print(s2)  # =&gt; xxbaaa</code></code></pre><p>The Python <code>str</code> <code>s1</code> is translated to Mojo <code>String</code> <code>s2</code> by calling the Mojo <code>String</code> constructor on it.</p><h2>Iterating over Python collections</h2><p>The <code>for</code> loop supports iterating over Python collection types. Each item retrieved by the loop is a <code>PythonObject</code> wrapper around the Python object, so you don&#8217;t need to dereference it like in Mojo collection <code>for</code> loops. The following snippet is a simple example of iterating over a mixed-type Python list and dict:</p><pre><code><code>    var py_list = Python.evaluate(&#8221;[42, &#8216;cat&#8217;, 3.14159]&#8221;)
    for py_obj in py_list:
        print(py_obj, end=&#8221; / &#8220;)
    # =&gt; 42 / cat / 3.14159 /
    print()
    var py_dict = Python.evaluate(&#8221;{&#8217;a&#8217;: 1, &#8216;b&#8217;: 2.71828, &#8216;c&#8217;: &#8216;sushi&#8217;}&#8221;)
    for py_key in py_dict:
        print(py_key, py_dict[py_key], end=&#8221; / &#8220;)
    # =&gt; a 1 / b 2.71828 / c sushi /</code></code></pre><div><hr></div><h1>Working with Python modules</h1><p>In this section we&#8217;ll show some useful Python modules at work within Mojo. Before calling into them, ensure your Mojo program can find them.</p><h2>Test if a Python module is installed</h2><p>An error may occur when attempting to import a module that isn&#8217;t installed locally. In Mojo, handle errors with <code>try</code> to run the dangerous code and <code>except</code> to catch the error.</p><p>Suppose we need the <code>pandas</code> module to work with data frames. Use the following code to avoid crashing your program when <code>pandas</code> (or any other Python module you want to use) is not available (see <code>importing_error.mojo</code>):</p><pre><code><code>from python import Python

fn main():
    try:
        var pd = Python.import_module(&#8221;pandas&#8221;)
        print(pd.DataFrame([1,2,3,4,5]))        #A
    except ImportError:
        print(&#8217;error importing pandas module&#8217;)  #B

#A Prints   
    0
0  1
1  2
2  3
3  4
4  5 
when pandas is available       
#B Print &#8216;error importing pandas module&#8217; when pandas is not available</code></code></pre><p>To test this, execute it first with <code>pandas</code> not installed, and then make the library available with <code>$ magic add pandas</code>.</p><h2>Making an HTTP request</h2><p>We need the <code>requests</code> module, so install it with <code>$ magic add requests</code>. The following code using the Python <code>requests</code> module shows how easy it is to make an HTTP request (see <code>http_request_from_py.mojo</code>):</p><pre><code><code>from python import Python

fn main() raises:
     var requests = Python.import_module(&#8221;requests&#8221;)
     var response = requests.get(&#8221;https://www.google.com/&#8221;)    #A
print(response.status_code)  # =&gt; 200
print(response.text)

# =&gt;
&lt;!doctype html&gt;&lt;html itemscope=&#8221;&#8220; itemtype=&#8221;http://schema.org/WebPage&#8221;   # lang=&#8221;pl&#8221;&gt;&lt;head&gt;&lt;meta content=&#8221;text/html; charset=UTF-8&#8221; http-           # equiv=&#8221;Content-Type&#8221;&gt;&lt;meta &#8230;

response = requests.get(&#8221;https://api.github.com&#8221;)
print(response.json())    #B

# =&gt;
&#8216;current_user_url&#8217;: &#8216;https://api.github.com/user&#8217;, &#8216;current_user_authorizations_html_url&#8217;: 
&#8216;https://github.com/settings/connections/applications{/client_id}&#8217;, &#8216;authorizations_url&#8217;: 
&#8216;https://api.github.com/authorizations&#8217;,

#A The get function retrieves the raw HTMl from the website
#B Or if the content is JSON-formatted, get it in that format with the json() function</code></code></pre><blockquote><p><strong>Note:</strong> If you want to work with a pure Mojo version, <strong><a href="https://github.com/Lightbug-HQ/lightbug_http">try out </a></strong><code>Lightbug_http</code>, which is a simple and small HTTP server made by the community.</p></blockquote><h2>Using NumPy and Matplotlib</h2><p>Many projects in scientific computing, data analysis, and machine learning use NumPy, an open-source Python library for efficiently manipulating large multi-dimensional matrices. A similar Mojo library is in the works, called <strong><a href="https://github.com/Mojo-Numerics-and-Algorithms-group/NuMojo">NuMojo</a></strong>, which is complementary to the Mojo <code>stdlib</code>.</p><p>When starting to migrate a Python project to Mojo&#8212;or if you&#8217;re a NumPy expert intent on using this library&#8212;you can call NumPy from Mojo. Let&#8217;s see it at work (see <code>numpy_graph.mojo</code>):</p><blockquote><p><strong>Listing 3.8 &#8211; Using numpy and matplotlib</strong></p></blockquote><pre><code><code>from python import Python, PythonObject

alias np = PythonObject     #A
alias plot = PythonObject

fn use_numpy(np: PythonObject) raises:
    var arr1 = np.array([1, 2, 3])     #D
    print(arr1)  # =&gt; [1  2  3]

    var arr2 = np.ndarray([5])         #D
    print(arr2)
    # =&gt; [4.67092872e-310 0.00000000e+000 0.00000000e+000 4.67150278e-310 2.37151510e-322]
    arr2 = &#8220;this will work fine&#8221;       #E 
    print(arr2)  # =&gt; this will work fine

    var arr3 = np.arange(15).reshape(3, 5)     #D
    print(arr3)
    # =&gt;
    # [[ 0  1  2  3  4]
    # [ 5  6  7  8  9]
    # [10 11 12 13 14]]
    print(arr3.shape)  # =&gt; (3, 5)

fn use_matplotlib(np: PythonObject, plt: PythonObject) raises:
    var arr1 = np.array([1, 2, 3, 4])
    var arr2 = np.array([30, 20, 50, 60])
    plt.plot(arr1, arr2)    #F
    plt.show()              #F

fn main() raises:
    try:
        np = Python.import_module(&#8221;numpy&#8221;)    #B
        plot = Python.import_module(&#8221;matplotlib.pyplot&#8221;)
        use_numpy(np)
        use_matplotlib(np, plot)
    except ImportError:&#9;&#9;            #C
        print(&#8221;Module numpy or matplotlib is not installed&#8221;)  

#A Make references to the Python modules as aliases.
#B Import the two modules inside a try/except.
#C Show an error when a module cannot be found.
#D Use the numpy array, ndarray and reshape methods.
#E Python is loosely typed.</code></code></pre><p>We applied the technique from the previous section to test for an error when importing the modules. In <code>use_numpy</code>, we showed the <code>array</code> and <code>ndarray</code> methods (used when more efficient processing is needed), and how to make matrices with <code>reshape</code>.</p><p><code>arr2</code> is clearly a Python variable: it can change its type from array to string, which is impossible for a pure Mojo variable.</p><p>Then we call <code>use_matplotlib</code>. There we make two arrays that form the X- and Y-axes of a graph, which is shown here:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Swoc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Swoc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 424w, https://substackcdn.com/image/fetch/$s_!Swoc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 848w, https://substackcdn.com/image/fetch/$s_!Swoc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 1272w, https://substackcdn.com/image/fetch/$s_!Swoc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Swoc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png" width="653" height="553" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:553,&quot;width&quot;:653,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28370,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/175610468?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Swoc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 424w, https://substackcdn.com/image/fetch/$s_!Swoc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 848w, https://substackcdn.com/image/fetch/$s_!Swoc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 1272w, https://substackcdn.com/image/fetch/$s_!Swoc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7f3dc90-095f-4aa9-a6e4-def37db44740_653x553.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 3.1 &#8211; Plotting a graph with matplotlib</em></p><p>Here is another example using the same pattern. After importing the necessary modules, it calculates a cosine graph and displays it with Matplotlib (see <code>py_mojo_simd.mojo</code>):</p><blockquote><p><strong>Listing 3.9 &#8211; Using numpy, matplotlib and SIMD</strong></p></blockquote><pre><code><code>from python import Python, PythonObject
from math import math                            #H

alias np = PythonObject
alias plot = PythonObject
alias size = 256
alias element_type = DType.float64

fn use_python(np: PythonObject, plt: PythonObject) raises:
    var py_result = np.linspace(0, 255, 256)      #A
    # print(py_result)
    # =&gt;
    # [  0.   1.   2.   3.   4.   5.   ...   253. 254. 255.]
    var simd_array = SIMD[element_type, size]()   #B
    for i in range(size):                         #C
        simd_array[i] = Float64(py_result[i])
    simd_array = math.cos(simd_array * (math.pi * 2.0 / 256.0))    #D
    # print(simd_array)
    # =&gt;
    # [1.0, 0.9996988186962042, 0.9987954562051724, ...

    var graph = Python.list()         #E
    for i in range(size):             #F
        graph.append(simd_array[i])
    plt.plot(graph)  &#9;           #G
    plt.show()  &#9;&#9;

fn main() raises:
    try:
        np = Python.import_module(&#8221;numpy&#8221;)  
        plot = Python.import_module(&#8221;matplotlib.pyplot&#8221;)
        use_python(np, plot)
    except ImportError:
        print(&#8221;Module numpy or matplotlib is not installed&#8221;)  

#H This imports everything from the math module
#A Use linspace from numpy to get an evenly spaced array of 256 values from 0. to 255.
#B Create a Mojo SIMD vector with size 256.
#C Copy the Python array to the Mojo SIMD vector.
#D Calculate the cosine values using SIMD and Mojo math functions.
#E Make an empty Python list object.
#F Copy the SIMD vector into the Python list.
#G Display the graph using matplotlib from Python.</code></code></pre><p>The curve is displayed in the following Figure 4.2:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!NP7D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!NP7D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 424w, https://substackcdn.com/image/fetch/$s_!NP7D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 848w, https://substackcdn.com/image/fetch/$s_!NP7D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 1272w, https://substackcdn.com/image/fetch/$s_!NP7D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!NP7D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png" width="756" height="434" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:434,&quot;width&quot;:756,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:34440,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/175610468?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!NP7D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 424w, https://substackcdn.com/image/fetch/$s_!NP7D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 848w, https://substackcdn.com/image/fetch/$s_!NP7D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 1272w, https://substackcdn.com/image/fetch/$s_!NP7D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478d4164-3ee8-4795-ac38-6cf09c9aebe5_756x434.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 4.2 &#8211; A cosine curve, calculated using numpy and SIMD and displayed with matplotlib</em></p><p>Note that because Python always returns <code>PythonObject</code>s, we must use a float conversion <code>float(py_result[i])</code> when copying the Python NumPy vector to the Mojo SIMD vector of size 256. The calculation is done on this SIMD vector, which is faster than doing it in NumPy.</p><p>You don&#8217;t believe me? Then try it yourself!</p><blockquote><p><strong>Experiment: Comparing a NumPy calculation and its SIMD equivalent</strong><br>Take the code from Listing 3.4 and add a Python calculation that does the same calculation on <code>py_result</code> as we did on <code>simd_array</code>. For this you&#8217;ll have to import the Python module <code>math</code>. The calculation itself must be done in a <code>for</code> loop.</p></blockquote><p>Then time this calculation with the <code>time</code> module from Python, as we did in Chapter 3 &#167;3.1.2.</p><p>Then also do the same for the Mojo SIMD calculation.</p><p><strong>Results (on my machine):</strong> Python takes 2.48 ms, while Mojo takes 0.00328 ms, which is 756&#215; faster!</p><div><hr></div><h1>Summary</h1><ul><li><p>Python code is processed at comptime by the CPython interpreter.</p></li><li><p>Python modules&#8212;local as well as from the ecosystem&#8212;can be used in Mojo through the <code>import_module</code> function.</p></li><li><p><code>PythonObject</code> forms the bridge between Mojo and Python. Through it, all Python types like <code>int</code>, <code>str</code>, <code>List</code>, and so on can be brought into Mojo code.</p></li><li><p>While pure calculations are better executed in Mojo using SIMD and other features, Python modules like NumPy, Matplotlib, Pandas, and many others provide easy and quick access to specific functionality that may not yet exist in Mojo.</p></li></ul><p>In the next part of the series, we will cover <strong>Compile-time metaprogramming in Mojo.</strong></p><div><hr></div><p>&#169; 2025 Ivo Balbaert. All rights reserved.</p><div><hr></div><p></p>]]></content:encoded></item><item><title><![CDATA[Building with Mojo (Part 2): Using SIMD in Mojo]]></title><description><![CDATA[Mojo&#8217;s SIMD-first model in practice: DType, fixed-size vectors, sys.info, and vectorize.]]></description><link>https://deepengineering.net/p/building-with-mojo-part-2-using-simd</link><guid isPermaLink="false">https://deepengineering.net/p/building-with-mojo-part-2-using-simd</guid><dc:creator><![CDATA[Ivo Balbaert]]></dc:creator><pubDate>Thu, 11 Sep 2025 08:28:42 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/79fa439e-7d92-49ac-b0a2-2be7b06591cf_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p><em>This article is Part 2 of our ongoing series on the Mojo programming language. <strong><a href="https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language">Part 1</a></strong> introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.</em></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;66ccb226-c67d-473f-87bc-d10b496c8804&quot;,&quot;caption&quot;:&quot;This article, kicks off a multi-part series exploring Mojo, a new programming language developed by the AI company Modular. Mojo is designed for AI-scale performance without abandoning Pythonic ergonomics. In Part 1, we&#8217;ll cover the following topics:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 1): A Language Born for AI and Systems&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-07-16T10:22:41.479Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e127b69e-ec68-43db-88a6-294f5ee6343d_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-1-a-language&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:168455891,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:1,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div></blockquote><p>Mojo is a high&#8209;performance language. One of the main features enabling that goal is support for parallel processing at several levels:</p><ul><li><p><strong>CPU calculations use SIMD automatically.</strong> A <code>vectorize</code> algorithm is available to support this data&#8209;parallel processing at a higher level.</p></li><li><p><strong>Its runtime enables task&#8209;parallel processing</strong> through automatic multithreaded code execution across multiple cores using the <code>parallelize</code> algorithm. This ensures maximum usage of all CPU cores.</p></li><li><p><strong>It supports execution on GPUs</strong> (which is intrinsically data&#8209;parallel processing) with the same syntax used for CPU execution.</p></li></ul><p>In this article we show the first way of parallelizing calculations in Mojo. Execution on GPUs will be addressed in a separate article.</p><p>We will first go deeper into what SIMD is and then demonstrate how Mojo works with it. We&#8217;ll talk about the following topics:</p><ul><li><p>What is SIMD?</p></li><li><p>Mojo is SIMD first</p></li><li><p>Retrieving and using SIMD system properties</p></li><li><p>What is a SIMD vector?</p></li><li><p>Working with SIMD vectors</p></li><li><p>The <code>@register_passable</code> decorator</p></li><li><p>Retrieving info about the host environment</p></li><li><p>Harnessing SIMD with vectorization</p></li></ul><p>The code in this article uses Python integration and calculations at compile time. We&#8217;ll discuss these topics more in depth in later articles. All code has been tested in Mojo&#8217;s latest stable version 25.4.</p><p>To follow along, first follow the instructions on <a href="https://docs.modular.com/max/get-started">https://docs.modular.com/max/get-started</a> to install pixi. Then use pixi to create a project called <code>simd</code> and <code>cd</code> into it. We are going to let Mojo work together with Python, so we need a running Python environment inside the Mojo project. Add a recent Python version such as 3.12 to the project with the command:</p><pre><code><code>$ pixi add "python==3.12"</code></code></pre><p>and start a pixi shell. To add the necessary Python library, run:</p><pre><code><code>$ pixi add matplotlib</code></code></pre><h2>What is SIMD?</h2><p>SIMD is short for &#8220;Single Instruction, Multiple Data.&#8221;</p><p>This tells us that SIMD is a way to process data simultaneously (in parallel). It takes advantage of special hardware registers called SIMD or vector registers in the CPU processors of the machine. Modern CPU cores, including those in desktops, servers, and even mobile devices, contain such registers that can perform calculations simultaneously on fixed&#8209;length vector data. This makes it possible for a single core to do up to 4, 8, 16, 32, or even 64 operations in parallel.</p><p>Their instruction sets have SIMD capabilities built in: Intel processors have SSE (Streaming SIMD Extensions) and AVX (Advanced Vector Extensions), and ARM Cortex&#8209;A and Cortex&#8209;R processors have the NEON SIMD instruction set.</p><p>For example, an Intel CPU with AVX&#8209;512 has a 512&#8209;bit vector register. This amounts to having 512/64 = 8 <code>Float64</code> data items on which we can perform calculations simultaneously. The theoretical speedup is 8&#215;, but in practice it will be lower due to memory read/write latency. The following schematic clarifies this:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gc5Z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gc5Z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!gc5Z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!gc5Z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!gc5Z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gc5Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png" width="550" height="550" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:550,&quot;bytes&quot;:1129065,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/173251283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gc5Z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!gc5Z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!gc5Z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!gc5Z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F015ba7cb-5d66-420c-b6ff-32086d4e1f4b_1024x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p><em>Figure 1: A schematic of how SIMD works</em></p></blockquote><p>Mojo has SIMD built in through the way its type system is set up. When you work with numerical types in Mojo, every operation is performed within SIMD registers.</p><p>Making an algorithm use SIMD fully is also called vectorizing. This is ideal for processing large amounts of data simultaneously, such as in image processing, artificial intelligence, and scientific calculations.</p><h2>Mojo is SIMD first</h2><p>Several languages (like C++, Rust, and so on) handle SIMD by using special libraries. But Mojo is unique in that SIMD is baked into the language: all numerical types and all built&#8209;in mathematical functions on these types use it automatically. Python works with references (a kind of pointers) for all variables, even for simple numbers. In Mojo, numbers (and other simple values) sit on the stack and can be passed straight into SIMD registers to be processed in bulk. We don&#8217;t need to look up these values on the heap via an address as in Python.</p><p>All the numerical data types that can be used in a SIMD vector in Mojo fall under the <code>DType</code> umbrella. <code>DType</code> is a built&#8209;in standard type defined as:</p><pre><code><code>@register_passable(trivial)
struct DType</code></code></pre><p>It contains the <code>Bool</code> type; 8&#8209;, 16&#8209;, 32&#8209;, 64&#8209;, 128&#8209;, and 256&#8209;bit signed and unsigned integer types (like <code>UInt32</code> and <code>Int256</code>); and 8&#8209;, 16&#8209;, 32&#8209;, and 64&#8209;bit floating types (like <code>BFloat16</code> and <code>Float32</code>).</p><p>For example, <code>DType.float64</code> means the same as type <code>Float64</code>, and <code>DType.uint32</code> means the same as type <code>UInt32</code>. Note that <code>DType.float64</code> is a value, while <code>Float64</code> is the real type.</p><p><code>Scalar[DType.float64]</code> is another alias for the type of a single <code>Float64</code> value.</p><p>The <code>@register_passable</code> decorator indicates that all of these types are handled through the SIMD registers.</p><p>SIMD should not be confused with parallel processing with threads executing on multiple cores. Each core has dedicated SIMD vector units, and we can take advantage of both SIMD and parallel processing to achieve massive speedup.</p><h2>Retrieving and using SIMD system properties</h2><p>To make the best use of the vector registers, it is important to know the SIMD capabilities of our target machine. The module <code>sys.info</code> contains a few functions to get that information (the results shown are for an Intel CPU with an AVX&#8209;512 register):</p><pre><code><code>from sys.info import simdbitwidth, simdbytewidth, simdwidthof

fn main():
    print(simdbitwidth())   # =&gt; 512
    print(simdbytewidth())  # =&gt; 64
    print(simdwidthof[DType.uint64]())   # =&gt; 8
    print(simdwidthof[DType.float32]())  # =&gt; 16</code></code></pre><p>The function <code>simdbitwidth</code> shows the total number of bits that can be processed at the same time on the host system&#8217;s SIMD register. The result 512 means that we can pack 64 &#215; 8 (= 512) bits, or 16 4&#8209;byte numbers, or 8 8&#8209;byte numbers together and perform a calculation on all of these with a single instruction.</p><p>The total number of bytes that can be processed at the same time is given by <code>simdbytewidth</code> (64 bytes on this machine).</p><p>Lastly, the function <code>simdwidthof</code> shows how many values of a certain type can fit into the target&#8217;s SIMD register. For example, use it to see how many <code>UInt64</code>s can be processed with a single instruction. On our system, the SIMD register can process 8 <code>UInt64</code> or 8 <code>Float64</code> or 16 <code>Float32</code> values at once.</p><h2>What is a SIMD vector?</h2><p>SIMD is a way to process data in parallel: all items in a SIMD vector (which is basically a list of numbers) undergo the same instruction simultaneously. Simply put, instead of one instruction processing one number, one instruction processes 4, 8, 16, 32, or more numbers at a time. SIMD is used in every calculation in Mojo, so understanding it in depth is very important.</p><p>Vectors are a basic type in any systems language. Their typical syntax is like <code>[1, 2, 3, 4]</code>, a numerical vector containing 4 elements. They are extensively used in calculations, which is why they are a first&#8209;class type.</p><p>In Mojo, vectors are SIMD&#8209;based and stored in stack memory, and they have a fixed size. A SIMD vector is defined as a struct with two parameters, type and size: <code>struct SIMD[type: DType, size: Int]</code>.</p><p>Mojo allows the definition of generic structs which have parameters declared between <code>[]</code>. Many structs from the stdlib take a type as a parameter. These parameters are processed at compile time to generate optimal code for that type.</p><p>Here is an example of a SIMD vector:</p><pre><code><code>var sd = SIMD[DType.uint8, 4](1, 2, 3, 4)</code></code></pre><p>Another way to define the same vector is:</p><pre><code><code>var sd2: SIMD[DType.uint8, 4] = [1, 2, 3, 4]</code></code></pre><p>The type of the vector&#8217;s elements is <code>DType.uint8</code> or <code>UInt8</code>. The size or length of the vector is 4, the number of elements. Because of the hardware registers needed for SIMD, the size of a SIMD vector must be a power of 2, so 1, 2, 4, 8, and so on are all valid sizes. Vector registers are the fastest kind of memory access at the hardware level, but their number is limited, in many CPUs currently at 32. For a machine with <code>simdbitwidth</code> 512, the limit is 64.</p><p>To process the maximum number of values of a certain type (here <code>Float32</code>) simultaneously, you would use an expression like the following:</p><pre><code><code>SIMD[DType.float32, simdwidthof[DType.float32]()]</code></code></pre><p>This defines a SIMD vector that processes 16 <code>Float32</code> values on a machine with a <code>simdbitwidth</code> of 512.</p><p>To explain how SIMD works, let&#8217;s compare the same operation (let&#8217;s say multiplying each element by 10) for a <code>List</code> with four elements 1, 2, 3, and 4, defined in Mojo as:</p><pre><code><code>var lst = List[UInt8](1, 2, 3, 4)</code></code></pre><p>and the SIMD vector <code>sd</code> from above with the same elements.</p><p>Using an ordinary <code>List</code>, we could use a <code>for</code> loop to do the multiplication, iterating over each item. Using a SIMD vector does the same work in only one operation, for all items at the same time. The following illustrates that principle:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Dcif!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Dcif!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 424w, https://substackcdn.com/image/fetch/$s_!Dcif!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 848w, https://substackcdn.com/image/fetch/$s_!Dcif!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 1272w, https://substackcdn.com/image/fetch/$s_!Dcif!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Dcif!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png" width="756" height="271" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:271,&quot;width&quot;:756,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24199,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/173251283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Dcif!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 424w, https://substackcdn.com/image/fetch/$s_!Dcif!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 848w, https://substackcdn.com/image/fetch/$s_!Dcif!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 1272w, https://substackcdn.com/image/fetch/$s_!Dcif!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9df9ecb4-d43c-4ec8-9604-afb5404a96d8_756x271.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p><em>Figure 2: SIMD processing: A SIMD vector is processed in one stroke, whereas a </em><code>List</code><em> has to be processed item by item.</em> </p></blockquote><p>A single instruction is processed across all its items at the same time (in parallel), instead of 4 separate instructions as when we use a <code>for</code> loop for the list. As an analogy: visualize the array of numbers as a bus with seats. Applying SIMD, with only one instruction, we can process the entire bus in the same amount of time it would have taken to process a single number in a non&#8209;SIMD way.</p><p>Obviously, this is faster than processing these items one by one from start to end of the list: the same calculation done with a SIMD vector is 750&#215; faster than a corresponding NumPy array calculation.</p><p>The following code shows the difference in processing all elements for a <code>List</code> compared to all elements of a SIMD vector:</p><p><strong>Processing all elements in a </strong><code>List</code><strong> and in a SIMD vector</strong></p><pre><code><code>var lst = List[UInt8](1, 2, 3, 4)  # Defining a List with 4 elements
print("[", end="")  # This print stays on the same line

# All elements of the List are processed one by one, in sequence, in a for-loop.
for ref i in lst:                  # i is a reference
    i *= 10
    print(i, end=", ")
print("]")
# =&gt; [10, 20, 30, 40, ]

var sd = SIMD[DType.uint8, 4](1, 2, 3, 4)    # Defining a SIMD vector with 4 elements
# Another way to define the same vector:
var sd2: SIMD[DType.uint8, 4] = [1, 2, 3, 4]
print(sd2)  # =&gt; [1, 2, 3, 4]

# All elements of the SIMD vector are processed simultaneously, in parallel
sd *= 10
print(sd)  # =&gt; [10, 20, 30, 40]</code></code></pre><p>To multiply every element in the <code>List</code> <code>lst</code> by a factor of 10, we have to go over the list with a <code>for</code> loop. The index <code>i</code> in the <code>for</code> loop is a reference and is made mutable by <code>ref</code>; it contains the address of the corresponding <code>List</code> element. Nothing of the sort is needed for the SIMD vector: we can simply use its name in every operation we want to apply on it, and the processing happens over all elements at once without going through a loop.</p><h2>Working with SIMD vectors</h2><p>Here are some basic operations with a SIMD vector:</p><pre><code><code>print(sd)  # =&gt; [1, 2, 3, 4]
print(len(sd))  # =&gt; 4
assert_equal(sd[2], 3)
sd[2] = 5
print(sd)  # =&gt; [1, 2, 5, 4]</code></code></pre><p>All operators like <code>*</code>, <code>/</code>, <code>%</code>, <code>**</code> can be applied to SIMD vectors of the same size and type; for example, multiplying two SIMD vectors of size 4 is done in one operation. Math operations on SIMD values are applied elementwise, on each individual element in the vector, but simultaneously. You can cast SIMD values to the <code>Bool</code> type with <code>Bool()</code> and then apply <code>&amp;</code>, <code>|</code>, <code>^</code>, or other boolean operators.</p><p>If you leave the <code>()</code> empty in the definition of a SIMD vector, you make a vector of zeros. If you fill in only one value, it is taken as the default for all elements:</p><pre><code><code>var zeros = SIMD[DType.uint8, 4]()
print(zeros)  # =&gt; [0, 0, 0, 0]

var ones = SIMD[DType.uint8, 4](1)
print(ones)   # =&gt; [1, 1, 1, 1]</code></code></pre><p>The <code>math.iota</code> function is used to fill up a SIMD vector with subsequent integers:</p><pre><code><code>var numbers = SIMD[DType.uint8, 8]()
print(numbers)  # =&gt; [0, 0, 0, 0, 0, 0, 0, 0]
# Fill them with numbers from 0 to 7
numbers = math.iota[DType.uint8, 8](0)
print(numbers)  # =&gt; [0, 1, 2, 3, 4, 5, 6, 7]

numbers *= numbers
print(numbers)  # =&gt; [0, 1, 4, 9, 16, 25, 36, 49]</code></code></pre><p><code>all</code> is a neat function to test a condition, which can be applied to all elements at the same time:</p><pre><code><code>x = SIMD[DType.uint8, 4](2, 4, 6, 8)
if all(x &lt; 3):
    print("all elements less than 3")
else:
    print("at least one element &gt;= 3")
# =&gt; at least one element &gt;= 3</code></code></pre><p>Some other commonly used methods are <code>join</code>, <code>interleave</code>, and <code>shuffle</code>:</p><pre><code><code>alias dtype = DType.float32

fn main():
    var a = SIMD[dtype, 4](0.5)
    var b = SIMD[dtype, 4](2.5)

    print(a.join(b))
    # =&gt; [0.5, 0.5, 0.5, 0.5, 2.5, 2.5, 2.5, 2.5]

    print(a.interleave(b))
    # =&gt; [0.5, 2.5, 0.5, 2.5, 0.5, 2.5, 0.5, 2.5]

    var c = SIMD[DType.int8, 4](42, 108, 7, 13)
    print(c.shuffle[1, 3, 2, 0]())  # =&gt; [108, 13, 7, 42]</code></code></pre><p>The <code>join</code> function concatenates two SIMD vectors, and <code>interleave</code> weaves their elements together. <code>shuffle</code> rearranges the elements according to the mask indices given inside the <code>[]</code>.</p><p>The <code>Scalar</code> type we talked about before, defining one number, is in fact a SIMD vector of size 1:</p><pre><code><code>alias Scalar = SIMD[size=1]</code></code></pre><p>This is shown here:</p><pre><code><code>s1 = Scalar[DType.int32](42)
s2 = SIMD[DType.int32, 1](42)
assert_equal(s1, s2)</code></code></pre><p>If we combine this with what we know from the <code>Scalar</code> type, we can state that <code>Int32</code>, <code>Scalar[DType.int32]</code>, and <code>SIMD[DType.int32, 1]</code> all mean the same thing. This shows again that all numerical types are intrinsically SIMD defined.</p><p>A Mojo idiom is to use the SIMD type and size as compile&#8209;time constants. We can define the type to be used (<code>element_type</code>) and the number of values (<code>group_size</code>) as aliases, as in the following listing:</p><p><strong>Using the SIMD type and size as compile&#8209;time constants</strong></p><pre><code><code>from sys.info import simdwidthof
import math

# Defining type and size as aliases
alias element_type = DType.int32
alias group_size = simdwidthof[element_type]()

fn main():
    # Initialize SIMD vector numbers with aliases
    var numbers: SIMD[element_type, group_size]
    numbers = math.iota[element_type, group_size](0)
    print(numbers)  # =&gt; [0, 1, 2, 3, 4, 5, 6, 7]</code></code></pre><p>Working in this way, the vectors you define fit maximally in the SIMD register, which benefits performance.</p><p>The stdlib module <code>complex</code> also defines a type <code>ComplexSIMD</code> and methods to work with complex numbers having SIMD backing.</p><h2>The <code>@register_passable</code> decorator</h2><p>Any Mojo type whose value is of type <code>AnyTrivialRegType</code> is register&#8209;passable. This means that its values can be used in SIMD registers, so we know such a type can be handled faster. These are also called trivial types, like <code>Int</code>, <code>Bool</code>, and any SIMD type. By default, they are stored on the stack.</p><p>We also have a way to indicate whether a value other than <code>DType</code> values&#8212;for example, the fields of a struct&#8212;can be processed in SIMD registers. This is done by annotating the struct with a <code>register_passable</code> decorator:</p><pre><code><code>@register_passable
struct Coord:
    # all fields can be passed into SIMD registers
    var x: UInt32
    var y: UInt32
    var z: UInt32</code></code></pre><p>When all the individual fields of a struct can be processed in SIMD registers, the struct as a whole can be passed into registers.</p><p>The generated code for the struct will then be optimized to work more efficiently.</p><p>Because the struct in our example has only fields of trivial types, we can make this clear by using: <code>@register_passable("trivial")</code>.</p><p>If used in combination with <code>@fieldwise_init</code> (which generates a constructor called <code>__init__</code> for all fields), the order of the decorators is important:</p><pre><code><code>@fieldwise_init
@register_passable
struct Coord:
    # &#8230;</code></code></pre><h2>Retrieving info about the host environment</h2><p>The more you know about the host machine your program will run on, the better you can tailor your code to gain speed or save memory. For example, you may want to know the type of your CPU and which specific features it has, which OS you&#8217;re running, and so on.</p><p>Based on this info, we could execute different code when we are on macOS compared to when we are running on Linux. Mojo has this info ready for you, stored as <code>Bool</code> values in <code>sys.info</code>, as you can see in the following code (results are for a machine that runs WSL2 in Windows 11):</p><pre><code><code>from sys.info import (
    os_is_linux, os_is_macos, os_is_windows,
    has_avx, has_avx2, has_avx512f, has_intel_amx,
    has_neon, has_vnni, is_apple_m1, is_apple_m2, is_apple_m3,
    _current_arch, _triple_attr,
    num_logical_cores, num_physical_cores,
    has_nvidia_gpu_accelerator,
    has_sse4
)

from sys import CompilationTarget

fn main():
    var os: String
    if os_is_linux():
        os = "linux"
    elif os_is_macos():
        os = "macOS"
    else:
        os = "windows"

    var cpu = String(_current_arch())
    var arch = String(_triple_attr())

    print("The host OS is  : ", os)
    print("Its CPU is      : ", cpu)
    print("Its Architecture: ", arch)
    print("Number of Physical Cores: ", num_physical_cores())
    print("Number of Logical Cores: ", num_logical_cores())
    # =&gt;
    # The host OS is  :  linux
    # Its CPU is      :  alderlake
    # Its Architecture:  x86_64-unknown-linux-gnu
    # Number of Physical Cores:  12
    # Number of Logical Cores:   24

    print("CPU-features:")
    if has_sse4(): print("sse4", end=" / ")
    if has_avx():  print("avx", end=" / ")
    if has_avx2(): print("avx2", end=" / ")
    if has_avx512f(): print("avx512f", end=" / ")
    if has_intel_amx(): print("intel_amx", end=" / ")
    if has_neon(): print("neon", end=" / ")
    if is_apple_m1(): print("apple_m1")
    if has_vnni(): print("avx512_vnni", end=" / ")
    if has_nvidia_gpu_accelerator(): print("NVIDIA-GPU")

    # =&gt;
    # CPU-features:
    # sse4 / avx / avx2 / avx512_vnni / NVIDIA-GPU</code></code></pre><p>A number of functions starting with <code>has_</code> or <code>is_</code> give info about the processor type and capabilities of the host system, among them are:</p><ul><li><p><code>has_sse4()</code>: SSE4 is the older SIMD instruction extension for x86 processors (introduced in 2006).</p></li><li><p><code>has_avx()</code>: AVX (Advanced Vector Extensions) are instructions for x86 SIMD support. They are commonly used in Intel and AMD chips (from 2011 onwards).</p></li><li><p><code>has_avx2()</code>: AVX2 (Advanced Vector Extensions 2) are instructions for x86 SIMD support, expanding integer commands to 256 bits (from 2013 onwards).</p></li><li><p><code>has_avx512f()</code>: AVX&#8209;512 (Advanced Vector Extensions 512) added 512&#8209;bit support for x86 SIMD instructions (from 2016 onwards).</p></li><li><p><code>has_intel_amx()</code>: AMX is an extension to x86 with instructions for special units designed for ML workloads such as TMUL, which is a matrix multiply on BF16 (from 2023 onwards).</p></li><li><p><code>has_neon()</code>: Neon (also known as Advanced SIMD) is an ARM extension for specialized instructions.</p></li><li><p><code>has_vnni()</code>: the host system has <code>avx512_vnni</code>.</p></li><li><p><code>is_apple_m1()</code>: The Apple M1 chip contains an ARM CPU that supports Neon 128&#8209;bit instructions and is GPU&#8209;accessible through the Metal API.</p></li></ul><p>Newer functions are regularly added; see <a href="https://docs.modular.com/mojo/stdlib/sys/info/">https://docs.modular.com/mojo/stdlib/sys/info/</a>.</p><h2>Harnessing SIMD with vectorization</h2><p>At the start of the article, we defined vectorizing as making an algorithm use SIMD fully. Fortunately for us, we don&#8217;t need to worry about the &#8220;making&#8221; part: Modular (the company that creates Mojo) provides a built&#8209;in <code>vectorize</code> function in the <code>algorithm</code> package.</p><p>In the following example we calculate 256 values of the cosine function and then plot the graph using Python matplotlib. The calculations are done in a SIMD array <code>tmp</code>. For storage we use an <code>UnsafePointer</code> called <code>array</code> with its <code>load</code> and <code>store</code> methods. The names defined with <code>alias</code> become compile&#8209;time constants. <code>@parameter</code> is a decorator for the <code>cosine</code> closure function, which ensures that the function runs at compile time and allows <code>cosine</code> to capture the array pointer.</p><p>The following code is executed on a machine where <code>simdbitwidth()</code> is 256, so <code>simd_with</code> gives 4, meaning 4 <code>Float64</code> values fit into the SIMD register at a time:</p><p><strong>Vectorizing the calculation of a cosine</strong></p><pre><code><code>from math import cos, iota, pi                 # Importing the necessary functions
from memory import UnsafePointer
from algorithm import vectorize
from python import Python, PythonObject
from sys.info import simdwidthof

alias size = 256
alias element_type = DType.float64
alias simd_with = simdwidthof[element_type]()

fn main() raises:
    # 1- Calculate the cosine array using vectorization:
    # Allocate array of size elements of type element_type
    var array = UnsafePointer[element_type].alloc(size)

    @parameter   # @parameter runs the function cosine at compile time
    fn cosine[simd_width: Int](n: Int):
        # Create a simd vector tmp of size simd_width
        var tmp = iota[element_type, simd_width](n)
        # print(tmp)
        # =&gt; [0.0, 1.0, 2.0, 3.0], [4.0, 5.0, 6.0, 7.0], until [252.0, 253.0, 254.0, 255.0]
        tmp *= pi * 2 / 256.0     # Convert radians to degrees
        tmp = cos(tmp)            # Calculate cosine
        # print(tmp, end="- \n")
        # =&gt; [1.0, 0.9996991239756597, 0.9987966769554031, 0.9972932019885731],
        # [0.9951896037943114, 0.992487148217141, 0.9891874614652418, 0.9852925291318769]
        # until [0.9948724987092192, 0.9970539358757885, 0.9986353937937995, 0.9996159208177102]
        array.store(n, tmp)       # Store the 4 calculated values in the UnsafePointer

    vectorize[cosine, simd_width](size)   # Vectorize the calculation

    # 2- Plot the cosine array using Python
    try:
        var plt = Python.import_module("matplotlib.pyplot")
        var graph = Python.list()
        for i in range(size):
            graph.append(array.load(i))
        plt.plot(graph)
        plt.show()
    except:
        print("error in plotting")
</code></code></pre><p><code>vectorize</code> is called as:</p><pre><code><code>vectorize[cosine, simd_width](size)
</code></code></pre><p>which here is:</p><pre><code><code>vectorize[cosine, 4](256)
</code></code></pre><p><code>vectorize</code> creates a loop calling <code>cosine</code> 64 times, 4 values at a time.</p><p>The <code>cosine</code> function is defined as:</p><pre><code><code>fn cosine[simd_width: Int](n: Int)
</code></code></pre><p>and will be called with these values:</p><pre><code><code>cosine[4](256)
</code></code></pre><p>Apart from the normal arguments enclosed within round brackets <code>()</code>, both of these functions also have data enclosed within square brackets <code>[]</code>. These are called parameters. If needed, these are calculated at compile time and then stored as constants to use during runtime execution.</p><p>The net effect of vectorizing is that, on a machine with a SIMD register size of 256, this will set 4 <code>Float64</code> values on each iteration, as we can see when we display <code>tmp</code>. This optimizes the effectiveness of the SIMD operations. The <code>tmp</code> values go from <code>n</code> to <code>(n + simd_width &#8211; 1)</code>, where <code>n</code> varies from 0 to 64.</p><p>This is the resulting plot:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HwmT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HwmT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 424w, https://substackcdn.com/image/fetch/$s_!HwmT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 848w, https://substackcdn.com/image/fetch/$s_!HwmT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 1272w, https://substackcdn.com/image/fetch/$s_!HwmT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HwmT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png" width="449" height="389" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:389,&quot;width&quot;:449,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28649,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/173251283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HwmT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 424w, https://substackcdn.com/image/fetch/$s_!HwmT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 848w, https://substackcdn.com/image/fetch/$s_!HwmT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 1272w, https://substackcdn.com/image/fetch/$s_!HwmT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4d5e6c4d-d0ab-459b-bff4-a1b214d4ba34_449x389.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p><em>Figure 3: (Plot showing the cosine function)</em></p></blockquote><p>The example from the previous section shows that vectorization simplifies SIMD&#8209;optimized loops. Here is the definition of <code>vectorize</code> from the stdlib:</p><pre><code><code>vectorize[origins: origin.set, //, func: fn[Int](Int) capturing -&gt; None, simd_width: Int, /, *, unroll_factor: Int = 1](size: Int)
</code></code></pre><p>Notice that <code>vectorize</code> is a higher&#8209;order function, which contains as a parameter a function with signature <code>func: fn[Int](Int) capturing -&gt; None</code>. It has one <code>Int</code> parameter and an <code>Int</code> argument. Our <code>cosine</code> function conforms to that signature, with <code>simd_with</code> as parameter and <code>n</code> as argument.</p><p>Vectorization works by mapping a function across a range from 0 to <code>size</code>, incrementing by <code>simd_width</code> at each step. The remainder of <code>size % simd_width</code>, if not 0, will then run in separate iterations.</p><p>Up next:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;f6eec920-4b92-4100-bf2a-a8c1e8f7b3df&quot;,&quot;caption&quot;:&quot;This article is Part 3 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 3): Python and Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-10-09T07:48:01.963Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f602cbb-615d-4272-91c3-2e88d5092315_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-3-python&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:175610468,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><p>&#169; 2025 Ivo Balbaert. All rights reserved.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Building with Mojo (Part 1): A Language Born for AI and Systems]]></title><description><![CDATA[An introduction to Mojo&#8217;s origins, design goals, and its promise to unify high-performance and Pythonic development.]]></description><link>https://deepengineering.net/p/building-with-mojo-part-1-a-language</link><guid isPermaLink="false">https://deepengineering.net/p/building-with-mojo-part-1-a-language</guid><dc:creator><![CDATA[Ivo Balbaert]]></dc:creator><pubDate>Wed, 16 Jul 2025 10:22:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e127b69e-ec68-43db-88a6-294f5ee6343d_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This article, kicks off a multi-part series exploring Mojo, a new programming language developed by the AI company <a href="https://www.modular.com">Modular</a>. Mojo is designed for AI-scale performance without abandoning Pythonic ergonomics. In Part 1, we&#8217;ll cover the following topics:</p><ul><li><p>Modular, MAX, and Mojo</p></li><li><p>The problem Mojo addresses in the software industry</p></li><li><p>The superpowers of Mojo</p></li><li><p>Mojo&#8217;s tooling and ecosystem</p></li><li><p>Where Mojo stands today</p></li></ul><h1>Modular, MAX, and Mojo</h1><p>Modular is a software company founded by Chris Lattner (of LLVM and Swift fame) and Tim Davies. Its mission is to build a next generation AI developer platform called <em>MAX </em>(short for: <strong>M</strong>odular <strong>A</strong>ccelerated <strong>X</strong>ecution), aimed at streamlining and simplifying AI development and deployment. MAX functions more like a framework, built upon the <em>Mojo</em> and Python. In fact, MAX&#8217;s core is written entirely in Mojo. The full platform is called the <em>Modular</em> software.</p><p>Modular&#8217;s business model involves deploying its software in enterprise environments and offering consulting services based on its deep technical expertise. However, The &#8220;Mojo Community Edition&#8221; which includes the Mojo language and the Max framework will always be open and free to use from their <a href="https://github.com/modular/modular">GitHub</a> repo.</p><p>Mojo is a new Pythonic system-level language. It adopts most, if not all, of the conventions and keywords used in Python, making it accessible to Python developers. At the same time, it allows lower-level programming using pointers, buffers, and tensors when performance is critical. Modular has committed to open-sourcing the entire stack: the standard library is already open, and the Mojo compiler is expected to be open-sourced in 2026.</p><p>Mojo&#8217;s development is spearheaded by Chris Lattner and his team at Modular, and the language was first made public in May 2023. The language is designed to deliver speed and adaptability to emerging hardware platforms, all within the Pythonic syntax familiar to AI and ML developers. It does this in two ways:</p><ul><li><p>Although software performance is notoriously difficult to benchmark, Mojo regularly matches and sometimes surpasses the speed of Rust and C++, as we&#8217;ll see in later sections.</p></li><li><p>Mojo is the first programming language that allows developers to target a wide range of hardware&#8212;including NVIDIA and AMD GPUs&#8212;without switching to CUDA. Unlike CUDA, which only works with NVIDIA GPUs, Mojo provides a unified programming model across platforms.</p></li></ul><h1>The problem Mojo addresses in the software industry</h1><p>Python is an excellent language when it comes to the design and prototype stage of machine learning and AI projects, offering a comfortable syntax and a wealth of supporting tools and libraries. Unfortunately, it&#8217;s much less suitable when moving these projects to a production stage, where you need high performance to satisfy customers and reduce costs. For such tasks, you needed, until now, to switch to a different language like C++ or Rust, which offer good low-level performance.</p><p>The Mojo language changes this fragmented workflow entirely: with its Pythonic syntax, memory-safety and C++ like performance, Mojo is well suited for both the development and deployment stage of AI or any other projects for that matter.</p><p>Python is the number one language used in machine learning and AI projects. Not only that, it is also rated as the most popular language in the <a href="https://www.tiobe.com/tiobe-index/">Tiobe index</a> and other language popularity rankings.</p><p>Python makes prototyping and modelling easy. Its almost pseudocode-like readability, mild learning curve and vast ecosystem account for its immense popularity.</p><p>But Python also has its downsides: most importantly, it runs slow and doesn't know how to run code in parallel, wasting all the core-power of current CPU's and GPU's. Many attempts have been made to remedy these problems, none of them fully satisfactory.</p><p>Performance in an AI project is crucial. When reaching the deployment stage, engineers often rewrite parts of or the whole Python-project in a low-level systems language, preferably C++. Adding to that difficulty, to get access to GPU's, parts of the model must be rewritten in CUDA, or special accelerator languages for more exotic processors. So, two or three languages are needed, and the project's infrastructure becomes increasingly complex.</p><p>There is an urgent need for simplification in this domain. Model development and deployment both must be possible in one high-performant language. Not only that, the language should also be able to work directly with GPU's and other processors.</p><p>To remedy this situation, <em>the industry needs a language that is both high-level like Python and low-level like C++ or Rust.</em> This seems impossible, but Chris Lattner and his team at Modular are working on exactly such a language called <em>Mojo</em>. Chris Lattner is most known for the creation of LLVM (Low Level Virtual Machine) and more recently Multi-Level Intermediate Representation (<a href="https://mlir.llvm.org/">MLIR</a>), alongside Swift, Clang and work on TensorFlow.</p><p>LLVM and MLIR are both compiler tool chains: as input they get program code, and as output they generate executable machine code for any processor instruction set. Between input and output, any number of transformation steps can be defined to optimize the final binary code for the target machine. LLVM has revolutionized the programming languages world since 2003, most notably with Julia, Kotlin, Rust and Swift. MLIR is a sub-project of LLVM and aims at making machine code even more adaptable to specific hardware. Both MLIR and LLVM are used in Mojo. Because of this foundation, <em>Mojo is the first programming language that can run on both CPU&#8217;s and GPU&#8217;s.</em></p><h1>An example of Mojo code</h1><p>Let&#8217;s now look at some Mojo code to get a sense of its syntax. The <code>main()</code> function is the typical entry point in a standalone Mojo program.</p><pre><code>fn main():
    words = List[String]("Mojo", "is", "fire")    #1
    for ref word in words:                        #2
        word += "&#128293;"                              #3
    print_list(words)                             #4

fn print_list(words: List[String]):
    for word in words:                            #5
        print(word)</code></pre><p>Every Mojo program needs a <code>main()</code> function to run, which, like all functions, can either be prefixed with <code>def</code> or <code>fn</code>. A <code>def</code> function is more flexible and Pythonic, <code>fn</code> is stricter and requires type annotations.</p><p>Line 1 defines a list of strings, named <code>words</code>. In line 2, we loop through each string <code>word</code> in the list. The <code>ref</code> prefix makes <code>word</code> mutable. Line 3 appends a <code>&#128293;</code> character to each string. Line 4 calls the <code>print_list</code> function, passing it the list. Finally in line 5, we loop through each <code>word</code> again and print each one.</p><p>For a Python programmer, this should look very familiar. But even if you don&#8217;t know Python, just from looking at the code, you'll probably be able to understand what it does.</p><h1>The superpowers of Mojo</h1><p>Mojo is <em>fast</em>, <em>scalable</em>, <em>safe</em> and enables <em>metaprogramming</em>. These seem like hype-terms, but Mojo is designed from the ground up to ensure these goals are met, providing developers with a great systems language with a familiar, readable syntax. In this section, we'll go through some arguments to substantiate these claims.</p><h2>High performance</h2><p>Performance comparisons show that Mojo easily surpasses Python, often by a factor of 10 or more, sometimes even by 4 to 5 orders of magnitude, beating the raw speed of C++.</p><p>For example, let us take a look at Aydyn Tairov&#8217;s port of a Llama2 model, first from C to Python, and then to Mojo (see <a href="https://github.com/tairov/llama2.mojo">GitHub repo</a>). Tairov benchmarked the model across 6 other languages under both single-threaded and multi-threaded cases. Mojo consistently performed well. In fact, the following benchmark shows Mojo outperforming C++, C, Zig, Rust, Julia and Go:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!boEj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!boEj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 424w, https://substackcdn.com/image/fetch/$s_!boEj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 848w, https://substackcdn.com/image/fetch/$s_!boEj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 1272w, https://substackcdn.com/image/fetch/$s_!boEj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!boEj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png" width="756" height="433" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:433,&quot;width&quot;:756,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:53711,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://deepengineering.substack.com/i/168455891?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!boEj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 424w, https://substackcdn.com/image/fetch/$s_!boEj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 848w, https://substackcdn.com/image/fetch/$s_!boEj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 1272w, https://substackcdn.com/image/fetch/$s_!boEj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4698e3a9-ee1b-400e-93c6-50ddd51196f4_756x433.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Figure 1.1 - Llama2 Inference on Mac M1 Max, multi-threaded [ stories42M.bin ] &#8211; Mojo outperforms 6 system languages in average tokens processed per second. Source: <a href="https://engiware.com/benchmark/llama2-ports-extensive-benchmarks-mac-m1-max.html">Llama2 Ports Extensive Benchmark Results on Mac M1 Max</a></em></p><p>This is no surprise, because Mojo is built on the latest modern compiler technologies like MLIR. Performance is further enhanced by using explicit types, for example, declaring that a variable num is of type Int (integer).</p><p>To understand why this matters, we need to look at Mojo&#8217;s compilation model and memory management.</p><h2>Compilation model</h2><p>Mojo compiles to <em>machine code</em> specific to the target platform, optimizing for every type in the code. This is done through the fast, next-generation <em>MLIR</em> compiler toolchain, which brings several innovations in compiler internals.</p><p>Mojo is the first language built entirely on MLIR. You could think of it this way: <em>Mojo is to MLIR as C is to Assembly</em>.</p><p>By translating into MLIR, Mojo automatically benefits from MLIR/LLVM&#8217;s execution speed optimizations.</p><p>The resulting machine code runs on a <em>high-performance concurrent runtime</em>. &#8220;Concurrent&#8221; means that the runtime can execute parts of your code in parallel, achieving even higher performance. The compiler itself is written in C++.</p><p>Mojo can be compiled in two ways:</p><ul><li><p><strong>Just in Time (JIT) compilation:</strong> Used in dynamic Python-like environments such as the Mojo Read Evaluate Print Loop (REPL) or Jupyter notebooks.</p></li><li><p><strong>Ahead of Time (AOT) compilation:</strong> Used when you need a standalone executable for a specific environment, for example in AI deployment.</p></li></ul><p>Python, by contrast, is interpreted by the CPython runtime.</p><p>When a Mojo executable runs, it executes binary machine code native to the target system. In JIT mode, the boundary is less clear: portions of code are compiled while code is already executing.</p><p>Python and Mojo can talk to each other: Python code embedded in a Mojo application is executed via an embedded CPython interpreter at compile time. You can think of this as <em>CPython talking to Mojo</em>.</p><h2>Memory management</h2><p>How memory usage is managed plays a major role in the speed and robustness of a language. Mojo possesses <em>automatic memory management</em>, which means that memory is cleaned up automatically in the most efficient and safe way without developer intervention. Unlike Java, C# or Python, Garbage Collection (GC) is not used in Mojo. As a result, Mojo doesn't suffer from GC pauses and can be used in real-time software. Memory is a precious resource in AI, so this is definitely a significant advantage.</p><p>Mojo is also designed for systems programming. It allows manual memory management using unsafe pointers, like in C++ and Rust. This enhances performance but decreases safety: the developer is responsible for managing memory manually when using unsafe pointers.</p><h2>Scalable and adaptable to new hardware</h2><p>Running a Python app in parallel is limited by the Global Interpreter Lock (GIL): Python can only execute one thread at a time, even in a multithreaded application. This means it can only use one of your machine's cores.</p><p>Mojo has no such limitation. It provides <em>inbuilt parallelization</em>, enabling automatic multithreaded code execution across multiple cores. This allows full utilization of the available CPU.</p><p>Through Mojo's LLVM foundation, a whole range of processor instruction sets (like IA-32, x86-64, ARM, Qualcomm Hexagon, MIPS, and so on) can be targeted.</p><p>On top of that, MLIR adds a layer of adaptability. That's why Mojo can potentially run on all kinds of hardware types, like CPU&#8217;s (x86, ARM), GPUs, TPUs, DPUs, FPGAs, QPUs, custom ASICs, and so on.</p><h2>Safety: a Python you can trust</h2><p>Mojo&#8217;s type system also improves error checking. Code becomes safer and more reliable, because far fewer problems occur at runtime; many are caught at. compile time instead. This increases confidence in your codebase.</p><p>Automatic memory management further improves safety, because it prevents all kinds of memory errors (like segfaults) and memory-leakage, which are a common cause of bugs in C++ applications.</p><p>The compiler tracks the lifetime of every variable and destroys its data as soon as it is no longer in use. This is known as <strong>eager destruction</strong> or <strong>As Soon as Possible (ASAP) destruction</strong>.</p><p>Mojo uses concepts like ownership, borrow-checking, and lifetimes, similar to Rust, but with simpler syntax.</p><h2>Metaprogramming</h2><p>Python is renowned for its dynamic and metaprogramming capabilities, but since Python is interpreted, this happens at runtime.</p><p>Mojo uses <em>compile-time metaprogramming</em> which comes at zero runtime cost, meaning it does not incur a performance penalty. Moreover, to metaprogram, you just use the same Mojo syntax as for "normal programming".</p><p>Here is a simple example that calculates SUM at compile time:</p><pre><code>alias SUM = sum(10, 20, 2)              #1

fn sum(lb: Int, ub: Int, step: Int) -&gt; Int:    
    var total = 0
    for i in range(lb, ub, step):       #2
        total += i
    return total

fn main():
    print(SUM)            # =&gt; 70       #3
      print(sum(10, 20, 2)) # =&gt; 70     #4</code></pre><p><em>Line 1</em> defines the alias <code>SUM</code> using the function call <code>sum(10, 20, 2)</code>.<br>The loop in <em>line 2</em> iterates over the values <code>10</code>, <code>12</code>, <code>14</code>, <code>16</code>, <code>18</code>, adding them up to get <code>70</code>.</p><p>The statement in <em>line 3</em> compiles directly to <code>print(70)</code>, because <code>SUM</code> is evaluated at <strong>compile time</strong>&#8212;it is an <code>alias</code>.<br>In <em>line 4</em>, the same sum is calculated at <strong>run time</strong> using a direct function call.</p><p>Metaprogramming in Mojo also allows for <strong>compile-time parameters</strong>, written in square brackets <code>[]</code>. These can be used in functions or structs. For example:</p><pre><code>fn greet_repeat_args(count: Int, msg: String):  #1</code></pre><p>The function in <em>line 1</em> uses both <code>count</code> and <code>msg</code> as runtime arguments.</p><pre><code>fn greet_repeat[count: Int](msg: String):       #2</code></pre><p>The function in <em>line 2</em> uses <code>count</code> as a compile-time parameter, and <code>msg</code> as a runtime argument.</p><h1>Mojo&#8217;s tooling</h1><p>A modern programming language needs a full-fledged IDE, with AI code assistance. Mojo has you covered on this front.</p><p>The Modular software is installed through the well-known package-manager <strong>pixi</strong>. The officially supported way for developing Mojo projects is Visual Studio Code with the Mojo plugin (get it at <a href="https://marketplace.visualstudio.com/items?itemName=modular-mojotools.vscode-mojo">here</a>), complete with LSP server for code completion, docs, function signature help, and so on. Mojo also works well with AI-assisted tools like GitHub Copilot.</p><p>The plugin provides for an <strong>LLDB debugger</strong>, which integrates nicely with the VSCode interface. Mojo has facilities for <strong>unit-testing</strong> and <strong>benchmarking</strong> built in.</p><p>To get started with a new Mojo project, use the <a href="https://docs.modular.com/mojo/manual/get-started/">Getting Started tutorial</a>. With pixi you create a Mojo project in a virtual environment, where the package dependencies and environment settings are automatically managed for you. This lets you maintain multiple isolated Mojo projects, each with its own toolchain and packages.</p><p>Mojo also uses the same conventions for its <em>module</em> system as Python. Additionally, modules can be pre-compiled into <em>packages</em>, which can then be reused across projects for a performance boost.</p><h1>Where Mojo stands today</h1><p>At the time of publication, the latest stable version of Mojo is 25.4, released on Jun 19, 2025. Mojo currently runs on Linux and macOS. On Windows, all features work through Windows Subsystem for Linux (WSL), while a full native port is a mid-term project.</p><p>Mojo is being worked on by a large, dedicated team at Modular and by an ever-growing group of external developers (over 260 developers have already contributed to the language). The community is active: the Mojo Discord server has over <strong>22,000 users</strong>, and the project has received more than <strong>24,300 stars</strong> on GitHub. The <a href="https://www.modular.com/community">community hub</a>, <a href="https://discord.gg/modular">Discord</a>, and <a href="https://forum.modular.com">forum</a> are great places to connect with other Mojo users and the Modular team.</p><p>The Mojo repository is open sourced under the <strong>Apache License v2.0</strong>. Currently, this applies to the full standard library and all documentation.</p><p>Mojo has many features that go beyond Python, even though it currently implements only a subset of Python&#8217;s syntax. Notably missing are the global keyword or user defined class types. However, support for even more Python features remains a long-term goal.</p><p>Mojo, of course, is not invented in a vacuum. Besides being a member of the Python family, Mojo benefits from insights and lessons learned from languages like Rust, Swift, Julia, Zig, Nim and Jai.</p><p>Mojo and the MAX engine currently run on <strong>Intel</strong>, <strong>AMD</strong>, and <strong>ARM </strong>CPUs, specifically:</p><ul><li><p>x86-64 (Intel and AMD, running Ubuntu 20.04/24.04 LTS or other Linux variants)</p></li><li><p>AWS Graviton3 ARM CPUs</p></li></ul><p>It also supports a range of <em>accelerators:</em></p><ul><li><p>Mojo GPU kernels for NVIDIA (A100, H100, H200, and any PTX-compatible devices)</p></li><li><p>AMD MI300X/MI325X</p></li></ul><p>All without needing CUDA.</p><p>A big chunk of the language has already reached stability. However, spearheaded by the new revolution in GPU programming, improvements are still being made all the time. A production-ready version is expected in <strong>Q1 of 2026</strong>.</p><p>Although AI is Mojo&#8217;s major goal right now, it is not built only for that. Its unique properties make it suitable for projects in areas such as:</p><ul><li><p><strong>Performance critical applications:</strong> High-Performance Computing (HPC), scientific/technical computing, data analysis and transformations, and high-quality web and other back-end servers.</p></li><li><p><strong>Embedded systems:</strong> IoT devices, mobile, automotive, or robotics, and hardware-integrated software..</p></li><li><p><strong>Low-level system programming:</strong> Device drivers, firmware, databases and operating system components.</p></li><li><p><strong>Game Development:</strong> Performance-intensive engines, physics simulations, and real-time graphics. AAA games need blazing performance and low-level control. Mojo offers both.</p></li><li><p><strong>Cryptography and Security-related applications:</strong> Secure communication systems, encryption algorithms, and cryptographic protocols.</p></li><li><p><strong>Other kinds of applications:</strong> CLI tools, database extensions, editor plugins, graphical native desktop UI's, and so on.</p></li></ul><p>A glimpse of what is possible can be found in the Mojo community project list at <a href="awesome-mojo">awesome-mojo</a>.</p><p>Community-built libraries include numerical tools (e.g., <strong>numojo</strong>, <strong>decimojo</strong>) and networking stacks (e.g., <strong>lightbug</strong>), alongside efforts in bioinformatics and high-energy physics.</p><p>In upcoming articles, we&#8217;ll explore more of what makes Mojo unique in depth::</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;e19cba4b-d8cb-4944-be68-74fbb206df5f&quot;,&quot;caption&quot;:&quot;This article is Part 2 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 2): Using SIMD in Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:true,&quot;bestseller_tier&quot;:null,&quot;primaryPublicationSubscribeUrl&quot;:&quot;https://ivob.substack.com/subscribe?&quot;,&quot;primaryPublicationUrl&quot;:&quot;https://ivob.substack.com&quot;,&quot;primaryPublicationName&quot;:&quot;Ivo Balbaert&quot;,&quot;primaryPublicationId&quot;:6364093}],&quot;post_date&quot;:&quot;2025-09-11T08:28:42.258Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/79fa439e-7d92-49ac-b0a2-2be7b06591cf_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-2-using-simd&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:173251283,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;5c9fcee6-56b1-408c-a89a-c1a02a27da4a&quot;,&quot;caption&quot;:&quot;This article is Part 3 of our ongoing series on the Mojo programming language. Part 1 introduced Mojo&#8217;s origins, design goals, and its promise to unify Pythonic ergonomics with systems-level performance.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Building with Mojo (Part 3): Python and Mojo&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:121154596,&quot;name&quot;:&quot;Ivo Balbaert&quot;,&quot;bio&quot;:&quot;I worked for 30 years in the software industry as a developer, project manager and programming teacher. Emerging programming languages have my main interest. I authored a number of books (Go, Rust, Julia and others) for Packt and other publishers. &quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33f3918d-d13b-4a6a-9761-1d2b5353c581_144x144.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-10-09T07:48:01.963Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f602cbb-615d-4272-91c3-2e88d5092315_1536x1024.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://deepengineering.substack.com/p/building-with-mojo-part-3-python&quot;,&quot;section_name&quot;:&quot;Building with Mojo&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:175610468,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:1729053,&quot;publication_name&quot;:&quot;Packt Deep Engineering&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!H5BJ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F736bc1ee-d689-497e-83a8-7d9bf9022eb9_600x600.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><ul><li><p>Compile-time metaprogramming in Mojo</p></li><li><p>Low-level memory operations in Mojo</p></li><li><p>GPU programming with Mojo</p></li></ul><div><hr></div><p>&#169; 2025 Ivo Balbaert. All rights reserved.</p><p></p>]]></content:encoded></item></channel></rss>