Is Mojo an application programming language or a systems programming language?

Note: some overlap with previous topic “Mojo as a Python superset”, but I think I have different points to make.

Is Mojo an application programming language or a systems programming language? Putting it another way, is Mojo for Python devs or Rust devs?

I ask because Mojo can’t be both. Every previous attempt at creating a universal programming language has failed: PL/I, Ada, Java. There was a time in the 1980s when PC/workstation applications were developed in C/C++, because there wasn’t any alternative, but that ended as soon as better application languages became available. Application developers don’t want the same constructs and concepts as system developers, which is why Python became so popular, so fast. Application developers will even use Visual Basic or PHP if it means they don’t have to use a systems programming language!

I started writing Mojo code at the end of 2024, because I wanted to write HPC code without C/C++. That was about the time Chris Lattner said, on YouTube somewhere, that the goal was to make only necessary changes from Python to Mojo. I gave a talk in Feb 2025 to the local Python User Group, basically “Mojo is a superset of Python and it’s really fast!”

Recently though, there have been changes which, as a Python application developer, do not seem at all necessary to me. There was “alias”, which is not a Python keyword but is a commonly used term, becoming “comptime” because that’s what Zig does? There’s the March 4 2026 “Heads up - explicit std imports” which, once out of alpha, will make Mojo incompatible with just about every Python program in existence.

A problem for me is that developing a new programming language is system programming and requires system programmers. I can’t contribute to a modern compiler backend for CPUs, let alone GPUs. All I can do is complain. (And no I don’t feel good about that.) The problem for Mojo is that programmers tend to develop for themselves, and from my viewpoint as a Python application developer there’s far too much discussion about Rust and not nearly enough about CUDA, let alone Python.

I want to remind everyone involved in Mojo that the number of Python developers who want to write HPC code is at least two orders of magnitude larger than the number of system programmers who want to write code with significant whitespace instead of curly braces. Whether the goal is to sell development tools or simply make developers happy, that should matter.

I am NOT saying that Mojo becoming a systems programming language is a bad thing. Systems programming, especially open source, is important and valuable. I personally am eternally grateful for Linux and Mesa3D. Nor do I think that what Chris Lattner said more than a year ago is a binding commitment to Mojo being a superset of Python. It’s OK to change goals.

So, I’m asking y’all, is Mojo an application programming language or a systems programming language? If the latter, I’ll walk away and you won’t have to put up with me complaining any more.

But if Mojo is for Python programmers, we have a gazillion lines of existing code. Even if Sturgeon’s Law applies to the quality, that’s still a lot of code we don’t want to rewrite.

2 Likes

Modular released a roadmap, at the moment they’re building towards Phase 1, which is a language for high-performance Kernel development.

For the changes and renames, the rationale is documented in the vision document, namely, Mojo will only innovate when the goal is not in existing languages. The main issue with the term “alias” is that it isn’t general, it doesn’t make sense to do: alias if or alias for, the goal was to generalise comptime metaprogramming into a single obvious terminology.

2 Likes

Mojo is meant for Python devs who need to get more performance out of their code.

Why all the Rust talk then?

  • Because Rust is a very recent example of a modern language that was built to improve on C/C++, similar to how Mojo is trying to improve on Python. Mojo can learn a few lessons from Rust’s journey.
  • Because Mojo is performance focused. Rust is one of the most performant languages - so Mojo can learn a few lessons.
  • Because Rust has ergonomic high-level abstractions for performant low-level code, which is also what Mojo wants to achieve. Rust does have some ergonomic issues around lifetimes and async, which is why Mojo is looking at Rust to attempt to improve on these issues with the origin system, and hopefully an improved async implementation.

Mojo is trying to learn from what Rust did right, and avoid what Rust did wrong.
But importantly, I don’t think Mojo wants to be Rust.

I don’t think Mojo wants to strictly be a systems language. Mojo just wants the performance of a systems language (with ergonomics approximating Python).

Modular’s main focus is AI, which is dominated by Python.
But the issue is that Python is slow, and the abstractions that makes Python fast, are difficult to write.

Mojo’s main focus is to create performant code that is more ergonomic to write via:

  • Performant default behaviour.
  • Flexible generics system.
  • Ease of writing your own abstractions. If I don’t like the ergonomics of an abstraction, I can write my own version of it.

The reason for building out these system programming features, is so that the average developer can build performant abstractions without having to write compiler-level code.
For example, Mojo’s reflection features make it a lot easier to write generic code - it makes me think of the ease of writing Ruby code.
People will be able to write the abstractions they care about if they put in a little bit more effort- that’s the main selling-point of Mojo, I believe.

Application languages, like Python and Java operate on top of low-level abstractions written in C/C++.
Mojo is currently building its version of low-level abstractions, as part of Phase 1 of the roadmap.

Phase 2 – "Once the core generic type system and systems programming features converge and stabilize, we’ll begin expanding Mojo to support application-level programming "

Phase 3 - “Eventually, we want Mojo to support the core dynamic features that make Python great, including support for untyped variables, classes, inheritance, etc.”

So Mojo will eventually be an application language, with systems programming features.

2 Likes

I think it’s useful to use the framework that Brian outlines in this talk.

At present, in my opinion, Mojo’s two mostly closely held values are first performance and second portability, with “python compatibility” falling somewhere below memory safety and learning from the mistakes of the past. I think that the “superset of python” marketing was always aspirational at best, and as time has gone on we have found more and more places where the decisions python makes conflicts with one or more of the more closely held values Mojo has. In those cases, python loses. Mojo will likely never be able to support exec, and supporting the ability to delete struct members would likely take adding structural types of some sort (aka much of the typescript type system).

I think you may have run into coming aboard early, and as a result you are seeing us build out the lowest level parts of the language, where everything is performance critical and needs to work on a GPU. On the data structure side, Mojo can’t even support a binary tree, and the internal implementation of a linked list has a bunch of messy origin casts that are UB waiting to happen.

I’m also not totally sure I agree with the premise. Applications have a habit of growing past their original scope, especially successful ones, and “systems languages” are called such because they are used to design large systems, where you need access to all of the tools you can get your hands on. For example, object pooling is a common tactic to help get around GC overheads in Java, C# and Go, but it’s really an imitation of an arena allocator. Java is now adding value types, and I’ve talked to a lot of Java developers who are finally winning their battles with GC spikes and memory usage as a result. To great applause, NVIDIA added raw pointers and manual memory management to Python in CuPY. The largest commonalities I see between languages commonly held to be systems languages are:

  • They are procedural (not functional)
  • They don’t have a garbage collector

To me, a lot of this is about the tradeoffs between incidental and inherent complexity. Incidental complexity is what happens when someone makes a bad API that’s more complicated than it needs to be, or where they don’t have enough “layers of complexity” where you don’t need to drop down further than what is required to get the features you want. Inherent complexity is complexity that shows up because some problems are hard, and trying to paper over it is how you get articles like “Why Discord is switching from Go to Rust”. When the language doesn’t offer facilities for going deeper, you end up in business ruining rewrites if you really need to go deeper to make something work. Right now, you’re watching the bottom layer (Pointers, Origins, traits, etc) being built, as well as the layer on top (Strings, basic data structures, etc). Once that foundation is solid, we can move up a few layers, to things functional style CoW data-structures with built-in reference counting, which mean if you’re willing to take a performance hit you’ll never see an origin. Thread safety can be fixed with ArcPointer[Mutex[T]] if you actually share things between threads, and I have plans for how to make Mojo not have the same work stealing issues that Rust’s tokio library has, which is the source of many of the “Async Rust” complaints.

In my opinion, one person’s “only systems people need that” is another person’s “this saved my application from performance issues”. Rust, which I think everyone agrees is a systems language, was found to be about as productive as Go (a language praised for being a “good enough performance” application language) by Google. So, if you can have your cake (the power of a systems programming language) and eat it too (application language level productivity), why not do that, especially if we can take advantage of hindsight to be even more ergonomic (and thus more productive) than Go? Systems languages are no longer limited by having to fit the compiler into 4 MB of memory, and they are flexible enough to offer safe abstractions if you choose to willingly accept performance costs. We need C++/Rust developers to help actually build the language and set a solid foundation, but expanding to more application-focused use-cases is on the roadmap and we could use people to keep us systems people in line as far as accommodating the less performance but more ergonomics case.

6 Likes

Indeed, I discovered this the hard way. A simple binary AST crashes the compiler without recovery or even a clear trace of what’s going on. The suggested hack with Unsafe pointers doesn’t work.

For me this is a deal breaker. If a language cannot represent dynamic recursive structures, it is not suitable for general programming purposes. The documentation should be upfront about this.

Using unsafe pointers should work as seen in the stdlib interval tree implementation. I should have been more clear, I meant that implementing __getitem__ for a tree requires using language escape hatches.

1 Like

That’s an excellent observation.

The first time I heard about Mojo was two years ago probably November 2024 but I joined the community late 2025 and that’s when I started learning and decided to join the Modular community to see what’s really happening and try to contribute. In which I’ve seen very plausible discussions.

Column 1 Column 2 Column 3 Column 4
What Makes Mojo a Systems Programming Language What Makes it an Application programming language
Low-level architectural performance - xPUs programming. High-level AI frameworks like MAX and Nabla try to make Mojo Pythonic
Rust and Zig like system architecture e.g std. memory implementation Vedio generation and image generation on MAX
C++ level or higher performance Pythonic syntax - e.g deprecation of fn to def.
Statically typed ecosystem Python Interoperability

Mojo is therefore a language that sits in the intersection of wether it’s a systems language or Application language.

But however it’s much more logical calling it a Systems Language. Mojo can achieve universal inference because it does almost everything from a high-level to compiler/machine level.

So I prefer calling it a Domain Agnostic Systems programming language.

Been reading the roadmap, and vision statement, and watched the “Platform as a Reflection of Values” And oh yeah, the discussion about integer division where again Python compatibility is abandoned so that 1 / 2 == 0 as the machine god intended.

My conclusion: Mojo is not for Python developers like me. Good luck and good bye.

1 Like

I think you you might want to revisit when phase 2 gets going. Right now, Mojo’s case is heavily centered around GPU programming and using MLIR to target more heterogenous AI accelerators, and on-device AI (Apple Silicon is an excellent start). The phase 1 language reflects that kind of low-level, “unsafe” primitives that are necessary for this layer.

For general purpose systems and application programming, there are a number of quality options developed over many years, from compiled, strongly typed cross-platform languages to Python itself that works great on desktop with Qt. I personally use eric 7 IDE with Qt PySide6 for cross-platform desktop. For the server side, and high performance client libraries, the Nim programming language serves me well. It is the closest to Mojo in terms of philosophy, and ergonomics. It is not a pressing need for Modular to go there. Mojo has a very clear and pressing case of solving the two-language tax and technical debt incurred by C++ for AI.

The open approach taken by Chris and the Modular team, and the communication in the vision document and roadmap are quite clear and inspiring. I am excited for Phase 2.

I would really love for Mojo to become the Goldilocks for systems + applications programming, and be available everywhere from mobile SoCs to data centers using LLVM/MLIR, but it can’t do so by being a proper superset of Python. It can be like Python deciding to find its mojo for these purposes though, and the rest handled by classic Python interop.

I think the community is troubled by the discrepancy between what the initial message was a couple of years ago (“we went with Python because that’s where the community lives”, “solve the n-language problem and have Python syntax everywhere” etc.) and what’s going on now: straying away from syntax, semantics, names, conventions so familiar to Python developers.

While I understand some compromises are absolutely necessary, and that the initial goal was frankly unrealistic, I think when a language decision needs to be made, the option that matches Python and is most familiar to Python developers is the best option. As it is now, the language is super confusing for Python developers: we use def but need raises, catching exceptions by type is not supported, integer division is quietly behaving different (unless the ints are literals!), Union is Variant, type composition uses & and works just for traits (maybe we’ll get | for unions I mean variants), I’m still confused about __str__ , __repr__, write_to, write_repr_to and so on. We can only hope that things will feel more familiar when introducing classes, dynamic dispatch, protocols etc. but I’m starting to expect things to look familiar but behave differently from Python. Also hopefully we’ll get Windows support and a standalone distribution with no Anaconda repos.

The idea is/was absolutely great, but somehow choosing a Pythonic syntax is starting to be an Achilles heel. Contrast this with Swift or CppFront - both having vastly different syntax compared to the status quo, or the flipside, with TypeScript. Hopefully we’ll not be left with this:

Speaking only for myself (not sure about “the community”), I think it is expected for a new entrant to be pivoting repeatedly in the course of its search for a product-market fit.

That being said, this topic clearly expresses the resulting confusion. The implied question - what does Mojo want to be when it grows up - remains unanswered.

(hint: It cannot be everything for everybody)

This is like C and Plan 9 C.

Actually, Mojo and Python feel like Swiss German/South-western German dialects to me :wink: They can sound dramatically different further along the continuum, but everyone context switches without thinking much about it. One just needs to become familiar. The gap is much wider when one considers Standard Swiss German (formal/written), and spoken Swiss German that are genuinely two completely different registers. Mojo is like the more vivacious Python à la spoken SG. The purpose is served by talking to the compiler.

I don’t think there is much for Mojo to answer about its purpose. The Python superset thing has been clarified now, and I come to think of it in the same way as C → Plan 9 C → Go. Go is immediately intuitive, and in fact, a simpler language to C programmers. Likewise, Mojo is going to be so much simpler than Python developers having to do C++ or Rust, while getting all the portability of Mojo (and MAX).

The syntax and semantics simply can not be directly applied between a very high-level, dynamic & interpreted language, and a compiled systems language developed around SIMD/Vectorization that would become more general purpose as it matures. Better to learn Mojo as a special dialect, being aware of traps that would trip Python devs over.