I agree, the LSP needs to be fixed. It’s often faster for me to run the compiler proper to get feedback.
Good point! Fully agree! ![]()
I’m still early in my Mojo journey, but right now, I think the thing I’m struggling with most is generics, and the paucity of examples of how to do anything reasonably sophisticated with them.
I’m experienced in using compile-time metaprogramming and generics in D and Rust, but finding it hard to transfer existing knowledge effectively despite the areas of overlap. It’s not entirely clear whether the challenge is purely lack of documentation or if there are also missing language features that are on a future roadmap.
I suspect I’m finding this grating because my concept of Mojo has been roughly, “Performance matching or exceeding C/C++/Rust/D, ease of use comparable to Python/D”, and so the fact that metaprogramming isn’t easy feels like a big wrench, and also a potential ongoing pain point.
There are a lot of missing things in Mojo right now, but you can think of traits like Rust traits but you can only use associated types. Can you give some examples of specific things you’ve tried to do with metaprogramming?
Hi @Serotonin , I’d love your feedback on this page welcoming Python developers. We also just launched this generics page and @arthur has been hard at work improving our metaprogramming/compile-time content.
I’m taking notes from all the feedback on this thread, so if you think of anything else, please let us know!
I’ve made a note about your bullet points.
@rd4com It’s definitely not meant as a production-ready list yet, but you might find the page on self-referential structs helpful here.
@JosephWakeling What are your thoughts on how Mojo names its metaprogramming elements, particularly the distinction between parameters and arguments?
Thank you; this is certainly helpful for newcomers. One thing I would suggest to work on is the parallelism between Python and mojo examples. For example when writing on mutability, the examples for Python and mojo do different things, and the example
fn foo(mut value: Int):
value += 1 # This works
does not correspond to Python’s
def foo(value):
value += 1 # This works
as the latter is equivalent to Mojo’s
fn foo(var value: Int):
value += 1 # This works
Instead, you may want to use a list as input and mutate it by extending it in this example.
One more thing: the intro/overview contains a lot of Mojo jargon and will be difficult to understand for a new person. If you add a link from each point to the corresponding section below, people know that they can find help directly in this document rather than needing to Google every other word.
Sorry for late response, it’s been a busy last few days.
It clicked pretty quickly that Mojo traits are pretty closely based on Rust traits, apart from differences in naming, which AFAICS stem from how Mojo uses inheritance to indicate that a type implements a trait.
I think that similar-but-not-the-same-notation aspect contributed a bit to my confusion: partly the minor bikesheddy hurdle of having to work out which Mojo traits corresponded to which Rust traits, but more that, the notation for doing things with them being not quite the same. It probably doesn’t help that I’m a little bit rusty (pun intended) on Rust generics, not having written a lot recently.
Anyway, a concrete example of something I’m struggling with: I have a struct whose internal state is SIMD[dtype, n] for some dtype and some integer n > 0. We can assume that n is fixed, not a generic parameter itself.
I was trying to implement a few different __init__ overrides. One that simply takes n parameters of Scalar[dtype] (or equivalent built-in); another that took A SIMD[dtype, n] parameter; and a third that took either an Iterable or Iterator instance, where the element type is equivalent to dtype.
In pseudo-code, it’d be something like:
def __init__[I: Iterator](out self, iter: I) where I.ElementType == Scalar[dtype]:
...
or with the option to parameterize Iterator directly (which I’d like, but OK, that’s syntax sugar):
def __init__[I: Iterator[Scalar[dtype]]](out self, iter: I):
...
or maybe even just:
def __init__(out self, iter: Iterator[Scalar[dtype]]):
...
I’m sure you get the idea. Either way, I ran into difficulties trying to work out the exact Mojo syntax to get what I wanted, which was to condition the existence of the constructor on having the correct element type. I suspect there may also have been an overload clash with any other constructor that took a single parameter.
I also found myself wondering if I shouldn’t be using Iterable instead of Iterator, except that there seemed to be no obvious way to condition on the element type in that case.
It’s quite possible I’ve probably missed some stuff elsewhere in the docs and in code examples, but the Generics page itself doesn’t seem to offer sufficient detail on how to deal with these kinds of case, where you want to restrict support to a subset of the possible trait implementations.
Another, related case would be if I want to implement a trait that has some comptime members, e.g.:
trait MyTrait:
comptime VarType: Movable
...
… how could I further condition that variable, such as requiring it to be an unsigned integer type, or perhaps a span of unsigned integer types?
It’s OK if the answer is “You can’t, yet, but it’s in the roadmap”, but the current generics docs don’t seem to consider the possibility that someone might want to do these sorts of things. They don’t e.g. mention some of the things you can do with is_{something} methods of DType, which seems to be the most effective current method to implement constraints based on built-in types.
The syntax of using square brackets for compile-time parameters, regular parentheses for runtime arguments is fine. It’s just another variation on all the different things that languages do – C++ with its <...>, Rust with something similar-but-not-quite-the-same, D with !(...) for compile-time parameters. Mojo’s choice fits nicely with Pythonic notation for its own generic parameters.
On naming: as I mentioned in my previous post, it chafes a little bit that traits are so similar in concept and design to Rust traits, but named differently. But that seems to be a consequence of Mojo’s choice to indicate trait implementation by inheritance. It’s ultimately bikesheddy for me to complain about this.
One thing I’m missing, relative to D, is the ability to define a compile-time template which in Mojo-esque notation would be something like:
def evaluate_something[...]: # note lack of runtime parameters!
# and here we do something that evaluates to a type
# or a comptime expression
See: Templates - D Programming Language for examples of what I mean, or Templates - D Programming Language and More Templates - D Programming Language (especially the latter) for a more how-to-use-them description.
Finally, I think I would appreciate an easy-to-find index of all traits provided either as builtins or in the standard library. (Apologies if this already exists and I missed it.)
Also, related to Iterable and Iterator:
- I’d like to see a
Boundedtrait as a more general version ofSized, which would implement theboundsmethod that’s currently part of theIteratortrait, withIteratoritself being rewritten to derive fromBounded. (I’d be happy to try implementing this if there’s any support for it.) Iterator.__next__is expected to raiseStopIteration. If I provide a__next__implementation that never raises, is it possible to detect this, and the consequent fact that the iterator is infinite?
Great feedback, thanks! I’ll see if we can update the docs to clarify this.
I think that’s a pretty good page that covers some of the nuances in instances where Mojo and Python are similar. I guess my biggest complaint is that it doesn’t really touch on where there are glaring differences from Python. I realize that it’s not meant to be exhaustive and is more of a “cheat sheet”, but looking over it I would get a false sense of security in how easy it would be to learn Mojo from Python because there’s little to no mention of those things that I mentioned in my first post:
comptimevalues and parameters- Generics, which I think should definitely at least be linked given Python’s “absence” of types.
- Origins
- Traits, especially given how reliant Mojo is on them
Perhaps, it would be good to include a section at the bottom highlighting and linking these as Recommended Learning, Deviations from Python, or something along those lines.
Minor Notes:
- In division operators I might add some examples of “mixed type” operations to illustrate how types are cast.
- In dictionaries it probably wouldn’t hurt to add an example with a
Variantas well. - In function definitions
->isn’t optional, unless it was meant to refer to the syntax in Python. - In error handling I might add a note that you can’t call the specific error type (yet). This is somewhat alluded to with the last sentence, but wouldn’t hurt to make it more explicit that, for example you can’t do
try:
test_typed_error()
except MyCustomError as e: # invalid
print(e.message)
- In the error handling example, the
fieldwise_initdecorator is used before explaining what it does. - Under types there isn’t any explanation as to what
unsafe_getdoes
What made you want to throw your hands up or walk away?
Lack of guidance on calling Mojo from C and other languages besides Python, and what’s possible/impossible at this moment.
I’m working on a more ambitious followup to From OpenCL towards Mojo GPU kernels in digital photo editing (first look) which involves rewriting performance critical parts of a C project and OpenCL kernels that come with it (Darktable) in Mojo.
Having a tough time understanding why the same kernel runs ok when the target is cpu, but segfaults on gpu specifically when called from a C program.
I might be doing something obviously wrong or have run into a known problem? - idk, can’t tell
I totally agree with this. It’s currently not clear at all from the documentation what kinds of external interop is supported (or even possible) when interfacing with non-Mojo code that may be written in OpenCL/Metal/CUDA/HIP/etc.
It would be very helpful to have a documentation page on what kinds of interop are supported and what kinds of interop are technically possible in Mojo, especially in terms of sharing device buffers, accessing zero-copy host buffers from device (when the hardware supports this), and sharing device stream handles.
The recent Int.__truediv__ change is so far the biggest frustration I have with Mojo. I literally had a “WAIT, WHAT?!” moment after updating, because it silently broke my code. I still hope this change will be reverted.
Must be the LSP! Selecting Python 3.10 from user or system space doesn’t work, which is pretty frustrating. But surprisingly, pointing it to .pixi/envs/default/bin/python3 works fine.
Also, the concepts of ‘Parameterizing’ vs. ‘Materializing’ always make my head spin when I’m switching between them.
I’m a Python application developer, especially HPC. Installed Mojo in late 2024, and was impressed. Gave a talk in Feb 2025 to the Canberra Python User Group which was basically “Mojo runs nearly all your existing Python code and it’s really fast!”
Had to stop for a while, but now I’m writing Mojo again. (Not being paid for it.) Delighted that I can now just pip installMojo, and that GPU support is in the standard library.
But, there are some “wait what?” changes.
First is using mut for pointer parameters, which you can read about in detail at this thread. Seems that re-creating the combinatorial complexity of C type definitions is intentional, not a bug.
And I had to change alias to comptime. Python does not have alias as an actual keyword, but the term is widely used to describe importing with a new name, and for type/class variables used in much the same way as C typedef. More formally, Python typing uses TypeAlias. The Mojo team have deprecated alias because, uh, some obscure language called Zig uses comptime? This change breaks my working program, and not for the better.
And finally (for this post) there’s the proposed? beta? change to imports from the standard library, which from now on will require qualification with std. Much discussion about Rust, and Cargo. No-one seemed to care that this would break Every. Single. Python. Program more complicated than “hello, world”.
Will write a longer post in a new thread. For now, I’m getting the impression that as a Python application developer, I’m in the wrong place.
My professional background is primarily in Python, but I’ve since moved to other languages over time.
I originally looked at Mojo as a “better Python” alternative, because I love the expressivity of Python.
Similar to you, I have gotten frustrated in the past with Mojo not taking the shape I hade originally assumed it would.
Over time I realised that the features the Mojo team is currently focused on, forms the foundation which Mojo will use to move closer to Python over time.
This can be a frustrating stance, because the future is subject to change, so we don’t know what shape Mojo will end up with.
And it feels like the current shape is a deviation from what we all got excited about - a faster, better Python.
However, I believe that the current work will allow Mojo to approximate Python in the future, while Mojo retains its performant foundation.
@laranzu I share your concerns, but I’m hopeful that Mojo will be:
- Something as useful as Python
- Something as approachable as Python (where starting is easy, but you can get more performance if you put in the effort)
- Something that works well with existing Python code.
Ultimately, complexity has to live somewhere -and that is what I believe the team is working on at the moment.
Given that it was to consolidate Int with the rest of the SIMD types, I highly doubt that it’s going to ever be reverted. You’ll have to manually cast to floats first in your calculations. My understanding is it’s the price we have to pay for peak performance.