Did Modular just reinvent Julia?

In Julia, the community has to interface with packages from the vendors for each kind of target device.
Whilst with MLIR, you can assume each vendor will do that for you. This will also be the case for Quantum Computing and things like that.

If Julia wants to stay competitive, it has to be able to compile to MLIR (in my opinion). Even if mojo fails, eventually there will be a MLIR language that does solve the 2 language problem like Julia does.

12 Likes

One of the main selling points of Mojo is compiled binaries. How would Julia implement compiled binaries for multiple dispatch? Since the number of methods for an unconstrained multiple dispatch call grows exponentially, the size of the binary would grow exponentially. Given the unconstrained Julia base, I don’t see how this problem can be solved. I know a company that solves it via PackageCompiler.jl and 100% coverage but that has a big problem, namely 100% coverage.

4 Likes

In fact, my recollection is that only two applications were mentioned in the docs (and they were repeated a few times): ML and systems programming.

In most (or many) cases, you are just interested in compiling for a single input type, or a single set of input types.

6 Likes

Does any other language solve the problem of generating small binaries without this constraint?

1 Like

But can you guarantee that all methods are compiled for all types flowing through the program? Differently put, not all Julia methods are inferrable, that is, some are inferred as Any. Can you guarantee that this Any output will, in practice, remain within the compiled branches?

More formally, can you guarantee that you have provided sufficient inputs such that you have found all possible output types for all functions. If there is some function f for which you only encountered f(::A)::B in the compilation and not f(::A)::C, then this can crash your program since C will be passed into some other function, which might have not seen C before during compilation.

1 Like

If every part of the program is inferable and type stable, why not? Then every type flowing through the program should be deterministic, and uniquely determined by the inputs.

Or else not.

I guess I don’t really understand your argument.

AFAIK it’s been mostly speculative discourse threads but people have suggested that precompiling can catch those Anys at type inference, and those sort of methods will have to be avoided if the JIT compiler is removed. Some have suggested that those methods could be accommodated by having the compiler do vtables for precompiled methods and throw an error if something unexpected comes up. But strict type stability seems to be more accepted, considering what small binaries are usually for. When the JIT compiler really is necessary, there are also ideas to reduce the PackageCompiler size but that’s still not really “small binaries.”

This should be impossible, or rather type inference should not miss the possible C even if it never happens at runtime.

1 Like

I just got access to the mojo test environment and am reading their “HelloMojo” document.

I’m not quite through the document, but my takeaway so far is this: if your primary goal is to take the large amount of python ML models that are out there and make them as portable as possible, this seems like a great tool for constructing that type of system. Modular is in the business of running ML models across many architectures; this tool feels specifically oriented around that goal, and I imagine it’s achieving that quite well.

I am somewhat surprised that they didn’t use Swift or Rust for solving these problems. I suppose it will be seen whether this can resolve the two language problem for existing Python users, especially outside of this use-case, but I have my doubts. I do not believe that Python users will move en-masse toward a compiled, typed systems language simply because it looks more like Python than C.

It’s taken a lot from Swift. For instance, values can be declared immutable by using let, or mutable by using var. Mojo also introduces struct, similar to that found in many languages.

where classes are extremely dynamic with dynamic dispatch, monkey-patching (or dynamic method “swizzling”), and dynamically bound instance properties, struct s are static, bound at compile time, and are inlined into their container instead of being implicitly indirect and reference counted.

Another alteration is method declaration. A user can implement a method with def just like in Python. But they can also implement the same method with fn. The semantics of the method call are the same, but fn calls must contain type signatures, and arguments are immutable by default. In other words, fn is more amiable to strict memory and type constraints often required for systems programming, like Swift:

def is defined by necessity to be very dynamic, flexible and generally compatible with Python: arguments are mutable, local variables are implicitly declared on first use, and scoping isn’t enforced. This is great for high level programming and scripting, but is not always great for systems programming. To complement this, Mojo provides an fn declaration which is like a “strict mode” for def .
fn and def are always interchangeable from an interface level: there is nothing a def can provide that a fn cannot (or vice versa). The difference is that a fn is more limited and controlled on the inside of its body (alternatively: pedantic and strict). Specifically, fn s have a number of limitations compared to def s:

This, to me, looks like Python + systems programming features, tackling a different set of problems from Julia, and with a very different approach. Essentially as advertised! I have no doubt it will find use and success, and I think as a language it will have strengths and weaknesses similar to Swift. It will be interesting to see how the community receives it and develops it.

11 Likes

what? statistics?

1 Like

if user wrote same amount of typing information in Mojo and Julia I don’t see why Julia can’t compile with the same strategy… if anything, Python is a much more flexible language than Julia.

Marketing doesn’t mean anything, I don’t see how you can just magically compile any Python code by some type hinting… there’s so much random stuff in Python user may rely on https://www.youtube.com/watch?v=qCGofLIzX6g

3 Likes

I think what’s being missed with most of these comments is that Mojo is not actually a Python accelerator like PyPy. Mojo is a completely new language with Python-like syntax (so, a lot like Julia). It will not be compatible with Python; instead, it’ll be interoperable, the same way Julia is, but their marketing material ignores this distinction. Python code in Mojo will work by calling the CPython interpreter, and will not be any faster.

As for the differences from Julia, I’ll bundle them Julia into 4 groups:

1-3: Could be done in Julia or a fork
  1. Static compilation of small binaries. They think this will let them skip doing all the crazy work the Julia devs have had to do to fix TTFX, since then libraries have 0 compile time. (Whether they’re right depends on how often they have to deal with method invalidations of library code, which are common in Julia because of package composability.)
  2. Bundles Python interop into the language. This could be done by adding PythonCall.jl to the stdlib, or adding it to a “Batteries-included” version of Julia with a bigger stdlib.
  3. Traits+interfaces–devs have wanted these a long time but haven’t had time to implement it.
4: Window dressing
  1. They’re using 0-indexing, semantic whitespace, and plan to include classes to make it look more like Python, and to make switching easier.

0/1-indexing honestly couldn’t matter less. The only good reason to use either one would be to make people stop complaining about it. Honestly, I would just add this package to base to stop wasting time answering questions about this.

UFCS would be nice for method discoverability, have ~0 downsides, and would go 80% of the way to solving most complaints about the lack of OOP.

The off-side rule matters even less than everything else, but probably has the biggest impact, and is kind of rational. The off-side rule is a shockingly good heuristic for readability. Think of Python, Nim, Elm, F#, and CoffeeScript. How do those compare to C, C++, Java, and JavaScript, or with R, Matlab, and Fortran for scientists? Julia (and Lua) are just outliers here. Python users think explicit blocks indicate “ugly languages,” because this is true of all the languages they know. (I almost decided not to learn Julia because of this!)

The Scala approach (insert missing curly braces implied by indentation during parsing, or for Julia, missing begin and end keywords) seems to have few disadvantages, can be done without breaking anything, and would probably get a lot more Python devs joining.

5: Shouldn't be done in Julia
  1. Removing multiple dispatch, polymorphism, and syntactic macros (the features that make Julia better than just “fast Python”). The Mojo devs don’t seem to have much knowledge of or interest in these features, from my conversations in their Discord.
6-7: ??? Dunno
  1. It was “built from the ground up to compile to MLIR.” Julia used to have support for this, but it broke and nobody’s tried to fix it. Currently, most Python ML code compiles to MLIR. The Mojo devs apparently think this requires a new language, but I have no idea why; by analogy, C compiles just fine to LLVM. But maybe MLIR is different.
  2. Replacing GC with a borrow checker, i.e. throwing errors at compile time if a possible memory leak is detected. It would definitely be great to have this as an option, as there are many (real-time) tasks that require memory management for high performance; this approach is great at preventing bugs, though. I have no idea how hard it’d be to implement this though. It would not be great to force users to use this for scripts; Mojo currently uses reference-counting in scripts, so I’m guessing the GC is going to be very slow if users try relying on it to save effort.
14 Likes

Type hints were standardized in September 2014. In March 2019 data one analysis found 20 percent of files in their open source sample were using at least some annotations. Presumably it’s more now.

In the corporate environment, Dropbox, Microsoft, Facebook, Google have all invested in making their own type checkers, which I imagine they’re running on a lot of code. Eg Our journey to type checking 4 million lines of Python - Dropbox

Company coders I don’t care, they can have an internal policy of 100% no template programming in C++ (google) and that means nothing to the wider users.

we’re talking about users here, do you think most users doing scientific computing use type hints? 20% is upper limit in 2023 is my estimate

2 Likes

The story I read from people familiar with Julia MLIR efforts is that MLIR was changing so rapidly that it wasn’t worth trying to keep up. And that now MLIR might be stable enough to pick it up again.

9 Likes

It definitely is–no ML ecosystem is going to be even close to competitive without an MLIR compiler. You may want to go through StableHLO, which is even more stable and offers more optimizations. Coil.jl lets you do this, but is not widely-known or used, and seems to be in early development. It’s probably not going to get any adoption unless it’s moved under a bigger org with the implicit promise that it’s going to be maintained into the future.

2 Likes

what makes you be so sure of this? I don’t think PyTorch uses MLIR and it’s competitive as an ecosystem on its own…

8 Likes

where do you cook up these super confident takes?

4 Likes

As far as I can tell both Tensorflow and Pytorch heavily interface with MLIR.

Multiple Vendors use MLIR as the middle layer mapping from platform frameworks like PyTorch, JAX, TensorFlow onto MLIR and then progressively lowering down to their target hardware.

1 Like

TLDR: In my opinion: Julia developers shouldn’t care about static compilation for type unstable code and code that does dynamic dispatch

If it is worth something to anyone, for me the perfect language has:

  • Multiple dispatch + parametric types
    Because I don’t think that Mojo can achieve the amount of modularity that Julia has without multiple dispatch. I do like that each mojo functions can have a @generated subpart to handle types. In Julia you often have to rely on constant propagation, which is hard to predict.

  • Decent syntax
    Julia’s syntax is perfect. I don’t want to write ** for exponentiation. I hate seeing self in code. And to me 1-based indexing is more natural.

  • Option to let the compiler type check at compilation time
    Like the difference between def and fn in mojo. There if you want your function to be type checked, you use fn, otherwise you use def. Maybe having the choice with a macro to let JET.jl run automatically is enough for this.

  • Compilation to binaries for multiple backends
    Maybe MLIR is needed for this, maybe not. In my opinion, this feature should only be available for Type stable code (i.e. fn in mojo). Julia developers shouldn’t care about compilation for type unstable code and code that does dynamic dispatch. I don’t think there are people that care about running dynamic code in production anyway.

(Note: I use Julia full time for half a year and I love it, this is ment as a way forward, not as critique)

15 Likes