People will go through any number of hoops to not give, "I like the feel of the language more", as a reason for using it. Clearly, the main reason you would consider using Elixir for this is just that. There may be benefits to Elixir as a language, but there is no way they are going to overcome the ecosystem deficiencies to make it worth while in any unbiased cost benefit calculation. Just say you want to work in Elixir! It's okay to have a preference on the tools you use, even if it's for aesthetic or experiential reasons.
> but there is no way they are going to overcome the ecosystem deficiencies to make it worth while in any unbiased cost benefit calculation
Is there a concrete reason for that? A lot of the approach in Elixir is to not go the route of other languages and merely slap an adapter interface on Python libraries. The approach is to approach things from a very Elixir and BEAM specific point of view. This is how Livebook was developed and how the machine learning libraries are being developed.
In fact, one could view it as Python being the one that's going to struggle to overcome the limitations set forth by its own language design. Elixir comes with very tightly integrated tooling (projects, testing, type analysis, static analysis, notebooks, scripting, etc.) that basically has 100% adoption rate without a plethora of third-party competitors and lack of integration with the language such as Python has. With immutability and concurrency built into the language, Elixir has a leg up, and there is active research into bringing static typing to Elixir and also binding Elixir to faster runtime environments, such as Rust.
Python simply cannot make the jump to Elixir, Erlang, and BEAM's language and VM capabilities like they can to a machine learning ecosystem akin to Python's.
People are willing to put in thousands if not millions of working hours into shoehorning things into the language they already know, unwilling to learn a language that already has a good set of basic principles and concepts, that would enable the thing they envision.
People will build huge C and C++ libraries for realizing distributed systems that they can then use from Python, instead of getting to know a language on the Beam VM, because they only want to stay in the mainstream ecosystems or are unwilling to deal with new concepts like in Elixir or Erlang (for starters lets say lightweight processes, distributed system, pattern matching, proper recursion (TCO), the functional programming view of things. Some of those are very scary for many Python developers.).
Fortunately there are those on the other side as well, stubborn enough to try to implement things in those more niche languages that they prefer, based on technological merit of basic principles and concepts of a language, rather than just doing what everyone else does. So sometimes something really great and interesting comes out of that.
Often however, the shoehorning masses are simply too many, the people knowing the other ecosystems too few, to keep up with them or the newest developments, further enabling the "but this is what everyone else is doing!" kind of mindset. Something something "beating the averages" here.
Maybe, but you make it sound like it's always some Blub paradox inspired reason. That someone doesn't want to learn thing <X> might be they're totally fine with <Y>; they can get work with it, they like working with it, whatever.
Not everyone is always looking for the next thing, better or not.
Static analysis in Elixir is quite more accessible because you rely way less frequently on dynamic dispatch than Python (or Ruby or JS). You typically know which module you are calling to, structs and pattern matching tell you about fields (which we verify at compile-time) and primitive type information. Things like deprecated and undefined functions are part of the compiler, while most other dynamic languages typically require type systems or linters (or do it exclusively at runtime).
I agree on type analysis though. It is better than nothing but that's not much to talk about. We are working on it. :)
Ok fair, compared to other dynamically typed languages Elixir’s static analysis is better, by virtue of existing at all :-) Something is better than nothing, but getting “function has no local return” when you make a particular kind of typo three modules down the stack is a long way from eg Java or TypeScript just telling you about the typo, instantly, right where you made it. That’s the norm IMO and that’s what I was referring to.
I’m excited to hear that Elixir may be getting a bit closer to that, and I agree with your observation that because typical Elixir code uses dynamic dispatch very little, there’s a lot of room for improvement in ways that eg Ruby will never be able to offer.
I’m sorry about the “terribly, laughably” bit of my comment.
For what is worth, I was not considering Dialyzer as part of my reply. You should be getting many warnings related to typos from the compiler!
Anyway I agree with the sentiment: we have a high ceiling but we are currently far away from it. If we had a Dialyzer that runs all the time, is fast (Erlang/OTP 26 already improved here), and has good error messages, it would already be great.
We are currently researching a proper type system into the language. Fully integrating and improving the Dialyzer experience would be our plan B.
Wow that would be great! I recall reading that an effort some years ago to do a type system on top of Erlang failed because typechecking inter process communication got too messy (or slow). Personally, I feel like a type system that leaves message passing completely out of scope would solve so many problems already. And then people can invent typechecked GenServers and the likes in userland.
I mean when 2 NodeJS processes communicate via JSON over HTTP then those aren’t typechecked either, even if their codebases are all TypeScript. Except, of course, when using the same TS rpc lib on both sides of an interface, with some code sharing between them so they use the same types.
I feel like even when all typechecking happens only on regular static synchronous function calls, and message passing is left out of scope, it’ll be so extremely useful. And like you said, the low amounts of dynamic dispatch makes it tractable. Still a huge amount of work I bet though :-)
Worse than Python? It's a question; I don't know. I do know that the two most terrible (for me; I like types and a working IDE that helps me, not fight me) languages (js/py) are also the most popular. I don't know what that means, but as I did build large systems with Erlang in the past (in banking) which are still running production with massive load (and don't fall down in 2 weeks because the ENTIRE ecosystem had a minor version update, invalidating all the ABI's), I cannot imagine it's worse than Python or JS.
Python has at least a couple of functional type checking systems. Elixir only has Dialyxer, which has never actually worked for me any time I’ve attempted to use it. (It’s not just me. At work we have a medium-sized Phoenix codebase and about every fifth person who joins is determined to add Dialyxer to our CI pipeline - so far without success.)
I use Dialyzer on all side projects and it always works. However, I enforce via Credo that everything gets a typespec, and I employ type-driven design.
Dialyzer and Credo for Phoenix does require some workarounds, as that project isn't interested in providing typespecs or base compliance with Credo and is very heavy on macros.
I believe you. However, the current Dialyzer/Dialyxir experience bad enough that the creator of Elixir is working on an effort to specify an entirely different† gradual typing system for the language:
This strongly suggests that Dialyxir is not working as effectively for most users as e.g. Typescript or Mypy.
† I can imagine people quibbling with 'entirely different', but the proposed type system does have a new(ish) theoretical basis that's still the subject of active research.
By type analysis, I mean Dialyzer and built-in type errors, and by static analysis, I mean Credo and its ability to define custom checks and Elixir's ability to access and walk its own AST plus the compiler features.
Yes, Dialyzer could be better, but it's pretty good if you write typespecs. But Credo is quite good and one of the best such tools I have ever used. And Elixir g enerally has excellent error messages. Why do you say it's laughably bad?
Also, the point was primarily that all these tools are effectively built-in to the language without several different competing libraries.
And I neglected to mention the official formatter.
My last weekend Project in Elixir is roughly 3 yrs ago now, but from what I recall the system was leagues better then pythons optional typing. It's just bad if you're used to languages that have been built around types from the start.
I think you’re just saying that you prefer dynamically typed languages. The point here is that Python now has much better options than Elixir if you want to check types statically.
I'm not sure how you could've gotten that idea from my comment. If I put it on a line, I'd say (no typing) < (python optional types) < (elixirs structs and pattern matching) < (typescripts types|interfaces) < (real static types from Java etc)
Hmm, ok, but structs and pattern matching aren’t static types. They certainly don’t offer anything like the kind of type safety you can obtain (with some effort) with mypy or other Python type systems. Mypy is quite similar to Typescript.
I think BEAM languages are kind of one of the few exceptions to this argument. Its tradeoffs and runtime semantics really are just very different from other mainstream languages. Other languages & communities are adapting to modern trends towards high concurrency, distributed systems, horizontal scaling, tight fault detection & recovery with varying degrees of struggle and success. Meanwhile in erlang these have been priority concerns for decades and they have highly sophisticated refined solutions to them built directly into the system.
Not that this article makes a good argument for the benefit of any of those things for ML. But at least generally while I am a pretty strong believer of "eh language doesn't really matter" erlang-based languages I do entertain a possible exception for.
> I think BEAM languages are kind of one of the few exceptions to this argument. Its tradeoffs and runtime semantics really are just very different from other mainstream languages.
Agree wholeheartedly. Elixir/BEAM/Erlang are a league of their own.
Never really played with the Erlang VM other than a little more than the Hello World in Elixir, but when I'm in the debugging hell of Apache Spark I really wish it wasn't based on the JVM.
I think debugging in Elixir is easier precisely because of it functional immutable nature. To contrast it, take Ruby (Ruby on Rails) where objects will acquire functionality at runtime. That makes debugging particularly difficult especially if you have jumped into a new codebase with little context.
Side effects free is a good thing? Sure. Easier to debug? Cmon, elixir MO is based on, if not copied from, ruby. If anything, elixir is as easy to debug as ruby, if you can claim such a thing (I don't have substantial XP)
That's flat out false. Only the syntax is taken from Ruby, that's it. Even the added metaprogramming isn't anything like Ruby's. It otherwise just like Erlang.
Not really. Elixir tries to use an approach to its "syntax design" that is similar to Ruby. But ruby goes all out in its approach to look like English. Elixir doesn't go that far.
Other than that if you look at how the language works under the surface it is completely different from Ruby which uses Runtime Reflection to add methods to objects in memory.
I was mentioning mostly debugging XP. And even if my comment applies to other facets of elixir, it does not describe it as a whole. That being said, I don't find it easier to debug than ruby, IME.
Oh, author here. I absolutely do simply prefer it but I also think it has distinct upside compared to Python in this context. I see a number of companies orchestrating their Python ML with Elixir. For them the advantage is even more obvious. But overall Elixir has certain strengths. It also has drawbacks and trade-offs.
I would absokutely write the post "Why I prefer Elixir" and I think I have. But this was not intended to be that.
The reality is that neither Python nor Elixir is performant enough to do the actual work of ML, that is all going to be wrapped libraries in C, C++ ... Rust?
And while Elixir may be as well suited as Python to being the interface/scripting language for ML tasks, Python has a pretty unassailable lead here with an overwhelmingly dominant ecosystem.
Elixir could be used to build and manage pipelines (like a roll-your-own Jenkins) in this area, and in this regard (concurrency, reliability, async) it is probably a significantly better language than Python, but the actual tasks on the pipeline will need to be defined in Python, just because it's use is so entrenched.
Also, the "reinvent something another language can already do" pitch is going to become a much harder sell in today's funding market as the silly money dries up.
A lot of comments here seem to be framed around the idea that Elixir is aiming to some day dethrone Python. Knowing the Elixir ecosystem, I wouldn't even say it's the case that it's trying to gain equal market share. The Explorer library even calls out: "The aim here isn't to have the fastest dataframe library around". There seems to be a significant interest within the Elixir community around this and, as a non-data scientist who needs to do some analytics sometimes, I certainly appreciate having powerful tools like this without having to step outside Elixir. I've never had to learn about Python tooling and I'm happy to keep it that way!
What hoops? Your comment makes it sound like the author is hacking together some libraries that happen to exist just to do ML in Elixir. Numerical Elixir is a very real thing being actively worked on by the creator of the language.
The only reason I discovered Elixir was through an unbiased cost benefit calculation while working on my last startup.
This was in early 2017, and my search essentially went from a variety of JS frameworks to RoR to Scala + Play and then eventually to Elixir + Phoenix. The ecosystem was much smaller then but of the options that were viable, it was by far the most productive.
I am just hoping, that when that better thing appears, it wont only be better because it makes use of what is available in Python via some FFI thingy, adding another layer of indirection and abstraction and overhead, but actually better from first principles, from the ground up, or at least not going via Python, to get a similarly big ecosystem.
I unironically think this is an absolutely legitimate reason even in a purely results-oriented, commercial context... but saying that is not politically expedient :(
The interesting thing about BEAM languages is that they naturally treat the gpu as an asynchronously communicating resource. No other language gets that abstraction correct.
V8 is asynchronous but single threaded, elixir gets m:n threaded. Job cancellation is trivial, safe, and automatically recovers the resource in elixir (the gpu devs have hooked cleanup in to the BEAM GC, because that is a thing). Moreover, if the caller of the agent asynchronously controlling the gpu is cancelled, it will also release the gpu automatically (in most cases, if you use the stdlib otp features instead of trying to build with primitives)
AFAIK V8 does make use of threads internally via libuv in order to make truly concurrent system calls. GPU device comms should fit perfectly in that design.
I decided to try to implement everything from scratch in Elixir (after initially doing all the math with pen and paper on a trivial example to get the feel of it). Obviously pure elixir was extremely slow, so I started creating NIFs to pass over matrix multiplication to OpenBLAS. Then I was thinking more and more of what things I can pass to C code and just have Elixir as a "frontend" for it. My enthusiasm died down when I realised I was simply implementing things in C with the pretext of "doing elixir", a nice learning experience but I could see I was not doing the things that initially got me pumped up.
Don't get me wrong, I loved the discovery part of it, reading research and trying to understand so I can implement the different new (at the time) deep learning techniques, like convolutions, LSTM, and the different nuances of it. I think it gave me a better understanding of how things work and why it works. But it deviated from the initial scope and I lost interest once the learning phase was over and I knew I could simply use tensorflow or pytoorch as I did not actually need the advantages BEAM offers for this type of workload.
That pretty much describes my Tcl experience in the first Internet wave, to the point I only care for languages with good JIT/AOT tooling in the box, leaving the rest for scripting activities.
"Adding ML to X" is a powerful way to probe what X is really made of and maybe even give direction to efforts to improve X.
That is because "ML" is a proxy for an entire domain (data science) that has both very distinct requirements and patterns developed over decades and is now evolving very fast as it broke out of the whitecoat lab and (for good or bad) into our lives.
The ascent of python is as the article noted "accidental" but that doesnt mean it is suboptimal given all the requirements of the domain and the available alternatives (here and now).
Its natural to focus on an exceptional attribute (like concurrency) to motivate even having the discussion but this addresses only one of three critical requirements if you decide to do everything within one langauge.
1) Ability to experiment interactively (REPL and friends), expressivity, human oriented language design etc
2) Ability to build production scale models (training or estimation phase) utilizing heterogeneous and fiddly compute
3) Ability to deploy to diverse devices and footprint / latency requirements both natively and as part of a web application. NB: Its only here that the intrinsic qualities of erlang might put it above the rest
Historically there was never an ecosystem than could do all that optimally within just one language. Some of the requirements are arguably even incompatible.
The current preferred solution is not "python" but python/c++ which does some of that well (1, 2) and 3 only passably.
The only declared single language contender is julia but its very far from being a complete ecosystem in the above sense.
Another emerging possibility is mojo, as a superset of python. But that is not even available yet.
Back to the erlang/elixir combo. Its not even clear to me if it can address all of the above in a passable way.
But on the other hand ML is really shaking the game and what is mayne more imprortant is the vitality and resourcefulness of different communities to develop in the direction of the new rewuirement.
Hello, creator of Elixir here (and co-creator of Nx).
I like your breakdown of the requirements in three abilities. I think Elixir provides all three of them (with a caveat) and I will refer to two videos for those who want to learn more:
* This video shows how you can use Livebook (open-source computational notebook environment) to explore pre-trained neural networks (from Hugging Face), learn how they work, and then deploy them as part of a Phoenix web application: https://news.livebook.dev/announcing-bumblebee-gpt2-stable-d...
The caveat is about your second ability. The production scale models are, at the end of the day running native/cuda code, using the same backends as Python (Google XLA and LibTorch). What we do is that we compile your Elixir code into a graph which is then compiled to the CPU/GPU just-in time (in an approach similar to JAX). You can find more information here: https://github.com/elixir-nx/#why-elixir
Personally I would love to see your vision succeed. Monocultures are intrinsically risky and limiting. Pretty soon we are all fish in a bowl, unable to see the water.
What will it take? I am just an armchair philosopher in this respect. Like they say languages are only in part about communicating with the computer, they are about communicating with other people.
So these three major tasks are really about winning over three different types of people and their peculiar brain wirings, motivations and aspirations. 1) and 2) are more introvert: mathematical types that want to express their inner abstract worlds in computer instructions in the least painful way. The famous data point is that pytorch became the tool of choice of academic ML publishing over the first mover tensorflow. Then you have the true computer nerds that love making the most of hardware and see in ML a major new stage of computing: HPC becoming mass market. 3) is more extrovert (keeping in mind the norms of the tech industry :-) developing and scaling delightful products and apps to be used by non-technical people.
Erlang/elixir clearly offers something special in terms of 3) and that is important because in the end its all down to serving useful software to people.
I'm curious, anyone have an anecdote where using Python for data processing or machine learning became a performance problem?
My experience is that Python has so many high quality packages to deal with cpu intensive tasks that unless you absolutely cannot use them, for some reason, performance is rarely a problem. But I'd like to be informed about the exceptions.
Not a single one of our data scientists knows how to use multiprocessing. Their jobs are all 1 CPU core, hundreds of gigabytes of RAM. The EC2 instances they run on end up wasting tons of cores, even with high-memory instances. This isn't necessarily Python's fault, but rather because Python has a lot of users who are not computer programmers.
One thing we can blame Python for is that the data scientists can't ever loop over their data with a regular loop. Simply impossible; it's too slow and would immediately wreck performance. They're more Pandas programmers than they are Python programmers; everything has to be done with Pandas operations for performance to be acceptable.
This limits our (the computer programmers) ability to help out, because the languages we use are not slow like that, so we may give suggestions from our world that simply can't be used in Python because it's too slow. "Can't you just loop over the data and do XYZ?" Nope, they can't.
We still use Python because it's all the data scientists know, and it's easier/cheaper to just write a bigger check to AWS than to hire people with both data science and computer programming backgrounds.
I've hit problems a couple of times with compute intensive data loading tasks in Pytorch.
One involved custom data augmentations on spatial graphs. I couldn't find any python libraries that offered performant implementations of the functions needed and ended up writing custom C++ extensions to call from python.
The other involved running video encoders and decoders inside the dataloader. The problem was more Pytorch specific than python per se, but the way Pytorch handles multiprocessing and CUDA streams made efficient parallelism very difficult. It would have been much easier in C++.
I've also needed to ditch python for ML inference in production when extremely low latency was required.
I've run into some pretty straight-forward problems (eg: modeling physical systems, with linear algebra and some other basic math) where avoiding python for loops meant allocating memory up front for massive numpy, and then doing multiple separate vectorized operations on them rather than fusing all of them into a single for loop. Required several hundred GB of RAM and took a few hours IIRC. This was of course still better than Python for loops... /facepalm
I wrote the same thing in Julia in about the same number of lines of code -- avoiding the sins above -- and I could run it on my laptop in a few seconds.
I hate the fact that if I choose Numba, then I can’t easily compose with other tools like autodiff, etc. Maybe Jax improves the situation in some aspects.
Generally though… While Python might have many distinct popular/well-developed tools, they tend to compose very badly. IME, it feels relatively painful if you’re trying to do something new which hasn’t been solved and wrapped for you.
As for Julia footguns, it has been relatively trivial IME to get substantial improvements compared to Python. Whether that is the best one can do, I dunno.
I wrote simulation software at a research department previously. 99.8% of the problems were perfect for numpy/pandas and learning a compiled language wasn’t worth it for the 0.2%. If I was to work the same problems again I’d do it in two years when polars has all the good stuff from pandas and I’d learn enough rust to drop down to it for the custom stuff.
At least for data processing pure python can quickly become a problem when handling somewhat large, dozens of GB, files. I've encountered this a few times and tge way to go is usually to either, if viable, port to a different language or only port the hot parts to a relevant library or Cython/Rust with pyo3.
Out of interest, did you find this was a problem that using optimised numpy still didn’t perform good enough? (depending on what you call pure python, yes I agree python loops etc are extremely slow)
I didn't find any problems with numpy based processing in practice. The issue more often is that users choose Python for ease of writing and the std lib and don't consider numpy for these cases. The performance considerarions usually only come up later.
I rarely see performance problems that are show stoppers, especially in the cloud, where you can always just buy a bigger node. Usually if something is going to be rewritten C++/Rust/$performant_lang it's that high velocity iteration on the codebase has mostly stopped and we could save 50-60% in infrastructure costs by running it on nodes with less resources. Then naturally you start to factor in the engineering manhours for the rewrite vs the potential savings.
I don't think Elixir/Nx needs to check boxes in an exhaustive point by point comparison with Python to emerge as a viable platform for ML work. It's a solid option, and that's enough for me to invest some time exploring.
I bought and I was working through the book, "The Handbook of Neuroevolution Through Erlang", 2013 Edition, several years ago, and then I tried to do the exercises in LFE (Lisp Flavoured Erlang). I am more a Lisper than a Rubyist, however, I do like Elixir. Gene Sher author of the book makes a great case for Erlang and ANNs and the book even has a preface by Joe Armstrong. I played with LUSH (Lisp Universal SHell) by Yann LeCun and others[1]. I wish they would have continued with Lisp too instead of moving to Python. Personally, I am now more an APL/J fan and the language being array based suits matrix math for ANNs very well. There are some nice papers that showcase this. I'd like to see advances in APL for ML. I am currently playing with April (Array Programming Re-Imagined in Lisp)[2] to scratch this itch. It gives you the general programming of Lisp to do the regular programming and APL to do the number crunching and matrix magic in terse code.
I get how array languages such as APL suit ML, and distributed languages such as those which run on the BEAM (I am now looking at Gleam, an ML-like language that runs on the BEAM. I prefer the syntax to Elixir's the same way I tried LFE first) and seem to fit ML. Python's angle is that it utilizes C/Fortran libs like LAPACK and BLAS and hooks to do what it does in ML, and Elixir's Nx is similar in that sense to get the performance in number crunching that the BEAM languages are horrible at. You can use Zig now to write NIFs for Erlang/Elixir so maybe this is how Elixir moves in the direction of Python's approach to speeding up the number crunching for ML along with Nx.
I prefer to try and use a more suitable language rather than try to make it work like Python/Numba/Numpy/Pandas and Elixir/Nx do. Funny thing is that Pandas by Wes McKinney was inspired by the J programming language. So I think you may not be with the crowd in my approach, but there is some harder-to-reach fruit in this approach that is more rewarding to me, and sometimes for others.
Great post. I'd be interested in a similar comparison when it comes to distributed computing/concurrency/fault tolerance/scaling/observability of Erlang/Elixir/Beam vs eg Rust or Go.
I wish I could not use python and other languages with def whatever.
Can’t be the only one to dislike these languages. I don’t even care about languages that much, but I avoid AI stuff because I just can’t stand the python or elixir ecosystems. So much crap is required to get something basic working, although it’s less annoying with elixir.
I don't know Elixir, but Python's syntax is excessively pedantic and boring. Functional programming is hardly supported (no real lambdas, currying is a gigantic hack, "match" is a statement and not an expression, ...).
So you are not the only one. Python is just good in tying CUDA libraries together while suffering through Python's syntax.
Just the syntax really. I work primarily with Go TS Dart. I just never liked the ruby/py syntax with def and lack of type safety.
I just stick to Go for all my backend needs. It does everything elixir is capable of and provides more than just raw IO handling without blowing up memory or struggling with computations. Never got into ruby either the syntax just makes me lose interest.
Makes sense! I on the other hand prefer more "human-like" syntax like Python and elixir provide, though I really dislike Python being so sensitive to whitespace (especially tabs vs spaces sensitivity)