This Week in Mojo 2023-06-16

Mojo Playground Update

Release Notes hereopen in new window

⭐️ New

Tuple type syntax is now supported, for example the following works:

fn return_tuple() -> (Int, Int):
   return (1, 2)

🦋 Changed

The TupleLiteral type was renamed to just Tuple, e.g. Tuple[Int, Float]

🛠️ Fixed

Community Content

Mojo Team Answers

Custom Allocators

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(n) vs arr.resize(checked = n) or something like that.

2023-06-16 Github Chris Lattneropen in new window

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 advocatesopen in new window. 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.

Self Hosting

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 ArrayRef and 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.

borrowed keyword

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.