This Week in Mojo 2023-06-16
Mojo Playground Update
Tuple type syntax is now supported, for example the following works:
fn return_tuple() -> (Int, Int): return (1, 2)
TupleLiteral type was renamed to just Tuple, e.g.
- Issue #354 - Returning a tuple doesn’t work even with parens.
- Issue #365 - Copy-paste error in FloatLiteral docs.
- Issue #357 - Crash when missing input parameter to variadic parameter struct member function.
- The team at kapa.ai created a GPT-4 powered chatbot for Mojo docs which you can now prompt from the #mojo-bot-help channel
- Put up a initial draft for the first chapter of a tutorial series Intro to Mojo: Basic Types. Feedback very welcome on GitHub issues.
- Cristian Adamo is working on a libc implementation in Mojo!
- yakupc55 has been experimenting with neural networks
- Alex1957 did a blog post on exploring vectors in Mojo
Mojo Team Answers
We don't have an established policy here and this is a really complicated topic, I'm not keen on making everyone always think about allocators like Zig does, I don't think that is practical in a language that cares about usability and ergonomics, but it is clearly good to allow folks to care.
In my personal opinion, there is a big difference practically between
node allocation and
array allocation. Error handling for small objects will kill us, and we don't want to make allocation of any class instance be failable. That said, allocating an array that could be 16GB definitely can fail. On the third hand, core data structures like Array probably don't want to expose memory allocation failability to the client by default for usability reasons.
It would be interesting to explore making these different APIs, possibly overloaded with a keyword argument or something. As one idea, we could make
UnsafePointer[T].allocate() non-failable, but make
UnsafePointer[T].allocate(Int) failable. We'd still have to decide what to do with that at the Array api level, but it too could have overloads for
arr.resize(checked = n) or something like that.
First Class Lifetimes
I'm optimistic the Mojo lifetime solution will be a nice step forward in both usability and expressivity vs rust, and first class lifetimes are very nice for inner pointers etc.
Mojo references are currently second class exactly as Graydon advocates. We're experimenting with lifetimes, but if they spiral in complexity we can always eliminate them as a concept and stay with the current design.
Untyped Mojo Improvements over Python
The easy answers are that the compiler eliminates a ton of overhead compared to the interpreter even if the individual operations are the same, and our dynamic object representation is a variant on the stack for simple things like numbers instead of a heap box, which is a huge win. We aren't doing any interesting static or dynamic analysis like V8 or PyPy etc yet, but we can obviously layer those things into the system as it matures.
It will take us quite some time to get there, but yes I would like the Mojo parser to some day be written in Mojo. I would also like to see the CPython interpreter rewritten in Mojo, but have no plans to do so. One can dream 😉
Receiver / Free Floating Functions
There are alternative ways to address the same thing, e.g. check out how extensions work in Swift. We'll need to look at this whole area as traits come in. We don't have a goal of providing the Julia multimethod dispatch thing. There isn't an efficient way to implement that other than full monomorphization, it is better to express the same thing with generics, which we haven't designed yet. Let's build out the traits system and see what the limitations are.
Side Effect Propagation
Unfortunately, it is pretty impractical to define what
side effect free means in a general purpose language; particularly one that wants you to be able to call existing python code.
In practice side effects would be so common that the model would have to be "add a keyword to opt-in/indicate/require that a function is side effect free", not "add a keyword saying it has side effects".
Given that, very few people would use it, and it would interfere with printf debugging and a lot of other things.
It's possible that there is a model here that will work and would be usable, but I'm not sure how much value it would provide.
Sorting Algorithm discovered by AlphaDev
Sure, that algorithm could definitely be used inside the Mojo sort algorithm. What they found is something you'd put into a standard library, e.g. they put it into the libc++ c++ standard library, eventually it could go into the Mojo stdlib.
StringRef from LLVM
Yep that's where it came from. It is directly related to string_view in C++, the LLVM data structures predate the C++ STL growing all these things. The idea of a
pointer + extend without ownership is more general than a
reference to a specific owning data structure because it type erases the concrete storage type. For example, an LLVM StringRef can point into C array, an std::vector, or one of the zoo of other specialized storage types llvm has - it can even point to a scalar on the stack.
Per the comments above, I think actually calling this sort of type
StringRef in mojo would be super confusing if we have
ref as a different concept. Python generally uses the word "Slice" for these things, and I think that would be great to use for these.
I don't have strong opinions, but I have some concern about general programmers (i.e., those without Rust experience) and the word "borrow". It is a word that can be explained and has good meaning in the rust lexicon, but doesn't connote referencing something, and doesn't even appear in the rust language (they use the & sigil instead). This isn't to say that "borrow" or "borrowed" is bad, but it does have some challenges.