11 ms·
Embedding Python in Elixir, it's fine
- crenwick 2y agoI feel like this project and blog post was made specifically for me. Can't wait to use this, thanks!
- pmarreck 2y agoLooks like a very cool way to interop with Python from Elixir without maintaining a separate Python stack (which is a PITA)!
- ddanieltan 2y ago“Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should” just kidding, this is pretty cool.
- qwertox 2y agoGreat and informative article. Also nice to get an explicit mention that this isn't just a subprocess call, but running in the same process. The only thing I'd would have like to see in added would be calling a function defined in Python from Elixir, instead of only the `Pythonx.eval` example. The `%{"binary" => binary}` is very telling, but a couple of more and different examples would have been nice.
- deleted 2y ago[deleted]
- jwbaldwin 2y agoI love the initial decision to grow Elixir's ML foundations from scratch, but I also love that we now have a really ergonomic way to farm out to the fast-moving python libraries > Also, it conveniently handles conversion between Elixir and Python data structures, bubbles Python exceptions and captures standard output Sooo nice
- behnamoh 2y agoElixir has some features I wish Python had: - atoms - everything (or most things) is a macro, even def, etc. - pipes |>, and no, I don't want to write a "pipe" class in Python to use it like pipe(foo, bar, ...). 90% of the |> power comes from its 'flow' programming style. - true immutability - true parallelism and concurrency thanks to the supervision trees - hot code reloading (you recompile the app WHILE it's running) - fault tolerance (again, thanks for supervision trees)
- ch4s3 2y agoMix is also so much better than anything python has to offer in terms of build/dependency tooling.
- mcintyre1994 2y agoJupyter might have fixed this now because it’s been a while since I used it, but Mix.install inline in Livebook (or any CLI script) is so much nicer than how installing Python dependencies in notebooks was last time I did that too.
- streblo 2y agouv for Python is a game changer, better than anything else out there and solves a lot of the core problems with pip/venv/poetry/pyenv (the list goes on).
- paradox460 2y agoI feel like you can write some variant of this comment every few years and just add the previous "best" to the front of the stack of things it's better than.
- fire_lake 2y agoIt’s true - people were saying that Poetry solves these problems for ages. Maybe uv does? I’ll wait and see.
- ejs 2y agoI love this, I've primarily been working in Elixir for a few years now and this is neat to see!
- jarpineh 2y agoAt first read this seems really promising. Getting into Elixir/Erlang ecosystem from Python has seemed too hard to take the time. And when there I wouldn't be able to leverage all the Python stuff I've learned. With Pythonx gradual learning seems now much more achievable. It wasn't mentioned in the article, but there's older blog post on fly.io [1] about live book, GPUs, and their FLAME serverless pattern [2]. Since there seems to be some common ground between these companies I'm now hoping Pythonx support is coming to FLAME enabled Erlang VM. I'm just going off from the blog posts, and am probably using wrong terminology here. For Python's GIL problem mentioned in the article I wonder if they have experimented with free threading [3]. [1] https://fly.io/blog/ai-gpu-clusters-from-your-laptop-livebook/ https://fly.io/blog/ai-gpu-clusters-from-your-laptop-liveboo... [2] https://fly.io/blog/rethinking-serverless-with-flame/ https://fly.io/blog/rethinking-serverless-with-flame/ [3] https://docs.python.org/3/howto/free-threading-python.html https://docs.python.org/3/howto/free-threading-python.html
- lawik 2y agoFLAME runs the same code base on another machine. FLAME with Pythonx should just work. FLAME is a set of nice abstractions on top of a completely regular Erlang VM. Chris Grainger who pushed for the value of Python in Livebook has given at least two talks about the power and value of FLAME. And of course Chris McCord (creator of Phoenix and FLAME) works at Fly and collaborates closely with Dashbit who do Livebook and all that. These are some of the benefits of a cohesive ecosystem. Something I enjoy a lot in Elixir. All these efforts are aligned. There is nothing weird going on, no special work you need to do.
- solid_fuel 2y agoYeah, looks like it works fine, here's an example: https://pastebin.pl/view/a10aea3d https://pastebin.pl/view/a10aea3d I'll add: FLAME is probably a great addition to pythonx. While a NIF can crash the node it is executed on, FLAME calls are executed on other nodes by default. So a crash here would only hard-crash processes on the same node (FLAME lets you group calls so that a flame node can have many being executed on it at any time). Errors bubble back up to the calling process (and crash it by default but can be handled explicitly), so managing and retrying failed calls is easy.
- bicx 2y agoFor Livebook, this looks really cool. Love that it calls CPython directly via C++ NIFS in Elixir and returns Elixir-native data structures. That's a lot cleaner than interacting with Python in Elixir via Ports, which is essentially executing a `python` command under the hood. For production servers, Pythonx is a bit more risky (and the developers aren't claiming it's the right tool for this use case). Because it's running on the same OS process as your Elixir app, you bypass the failure recovery that makes an Elixir/BEAM application so powerful. Normally, an Elixir app has a supervision tree that can gracefully handle failures of its own BEAM processes (an internal concurrency unit -- kind like a synthetic OS process) and keep the rest of the app's processes running. That's one of the big selling points of languages like Elixir, Erlang, and Gleam that build upon the BEAM architecture. Because it uses NIFs (natively-implemented functions), an unhandled exception in Pythonx would take down your whole OS process along with all other BEAM processes, making your supervision tree a bit worthless in that regard. There are cases when NIFs are super helpful (for instance, Rustler is a popular NIF wrapper for Rust in Elixir), but you have to architect around the fact that it could take down the whole app. Using Ports (Erlang and Elixir's long-standing external execution handler) to run other native code like Python or Rust is less risky in this respect because the non-Elixir code it's still running in a separate OS process.
- chefandy 2y agoI hadn’t heard of gleam. Looks cool! I like working with elixir in a lot of ways but never was a Ruby guy, and I think I’d prefer the C-style syntax.
- giancarlostoro 2y agoI'm more of a Python and C# kind of guy, so Elixir never really hit the itch for me, but Gleam definitely does. One of these days I'll take a crack to see how I can use Gleam with Phoenix.
- chefandy 2y agoI’ve been mostly in Python, C# and C++ for the past decade or so but got into Elixir as my first functional language. Never got comfy with the syntax but dig how everything flows. Looking forward to digging into Gleam.
- cpursley 2y agoReally glad to see this, Elixir has languished in the AI wars despite being a better fit than JavaScript and Python.
- tombert 2y agoForgive some ignorance on this; why is Elixir a better fit for AI than Python or JavaScript? I'm not disagreeing, I've just never heard that, I didn't think that Elixir had good linear algebra libraries like NumPy.
- jyscao 2y agoIt does now with Nx
- cpursley 2y agoSorry, I should have been more explicit: better for on the user facing implementation side (concurrency, streaming data, molding agent state, etc) vs the training side of things. If that makes sense.
- tombert 2y agoAh, fair enough. I've not done much with Elixir but I have done a fair amount with Erlang and you certainly don't need to sell me on how great it is for concurrency and distributed stuff.
- solid_fuel 2y agoI’ve been actively using elixir for ML at work, and I would say it’s a solid choice. The downside - unfortunately while bumblebee, Axon, and Nx are libraries that seem to have a fantastically engineered base most of the latest models don’t have native elixir implementations yet and making my own is a little beyond my skill still. So a lot of the models you can easily run are older. But the advantages - easy long running processes, great multiprocessing support, solid error handling and recovery - all pair very well with AI systems. For example, it’s very easy to make an application that grabs files, caches them locally, and runs ML tasks against them. You can use process monitoring and linking to manage the locally cached files, and there’s no runtime duration limit like you might hit in a serverless system like lambda. Interprocess messaging means you can easily run ML in a background task and stream results asynchronously to a user. Additionally, logs are automatically streamed to the parent process and it’s easy to tag logs with process metadata, so tracking what is going on in your application is dead simple. That’s basically a whole stack for a live ML service with all the difficult infrastructure bits already taken care of.
- 4b11b4 2y agoyes
- lawik 2y agoAs someone very involved in Elixir and who used to do a lot of Python this seems very practical for me. I'm actually even more interested in that Fine library for making C++ NIFs easy. That seems ridiculously valuable for removing hurdles to building library bindings.
- djha-skin 2y agoElixir is just Lisp with a facelift[1], and lisps can be built on Python[2]. It stands to reason that an elixir-like can be built on Python too, so you could embed the Python runtime in Elixir but Elixir-likes are used to code for both. 1: https://wiki.alopex.li/ElixirForCynicalCurmudgeons https://wiki.alopex.li/ElixirForCynicalCurmudgeons 2: https://hylang.org/ https://hylang.org/
- pjmlp 2y agoIn a way Python is a bad Lisp, still looking forward that catches up in native code compilation and multiline lambdas. Could be better, but that is what mainstream gets.
- kazinator 2y agoWe can sort of make a shitty multi-line lambda in Python by making a dummy function called progn, and using it, like this: >>> def progn(*args): ... if args: ... return args[-1] ... else: ... return None ... >>> fun = lambda x : progn(print('abc'), ... print(x), ... print('def')) >>> >>> fun(42) abc 42 def :)
- ch4s3 2y agoThe operating environment of the BEAM is what's great about elixir. Hy still has the GIL.
- chantepierre 2y agoI love to see "well-known" people in the Elixir community endorsing and actively developing that kind of approach. Our VM and runtime does so much and is so well suited to orchestrating other languages and tech that it sometimes feels there's a standard track and an off-road track. The difference between an off-road "sounds dangerous" idea and its safe execution is often only the quantity of work but our runtime encourages that. Here, it's a NIF so there's still a bit of risk, but it's always possible to spawn a separate BEAM instance and distribute yourself with it. Toy example that illustrates it, first crashing with a NIF that is made to segfault : my_nif_app iex --name my_app@127.0.0.1 --cookie cookie -S mix iex(my_app@127.0.0.1)1> MyNifApp.crash [1] 97437 segmentation fault In the second example, we have a "SafeNif" module that spawns another elixir node, connects to it, and runs the unsafe operation on it. my_nif_app iex --name my_app@127.0.0.1 --cookie cookie -S mix iex(my_app@127.0.0.1)1> MyNifApp.SafeNif.call(MyNifApp, :crash, []) Starting temporary node: safe_nif_4998973 Starting node with: elixir --name safe_nif_4998973@127.0.0.1 --cookie :cookie --no-halt /tmp/safe_nif_4998973_init.exs Successfully connected to temporary node Calling MyNifApp.crash() on temporary node :error iex(my_app@127.0.0.1)2> Thankfully Python, Zig and Rust should be good to go without that kind of dance :) .
- tommica 2y agoIts a neat way to do it - spin a temporary one, which can crash all it wants without affecting the other nodes. Fits like a glove to BEAM.
- incontrol 2y agoI was super excited until I read: "...if you are using this library to integrate with Python, make sure it happens in a single Elixir process..."
- nesarkvechnep 2y agoWhat’s the problem? You plan on running multiple instances of you Elixir program?
- incontrol 2y agoI'm using Python script in my Phoenix app (not Livebook). I hoped Pythonx would solve all the issues associated with System.cmd, but as you can imagine, I have more than one user.
- yellowapple 2y agoOther commenters have already pointed out the safety implications of using NIFs for this. There are, however, other downsides worth considering: - The Erlang VM scheduler can't preempt a NIF, so a long-running Python call risks hanging the VM. This is a non-issue for ports, since Python's running in a separate OS process. A NIF can mitigate this by spawning an OS thread and yielding until it finishes; ain't clear if that's what this library is doing. - The article already mentions that the GIL prevents concurrent Python execution, but this would also be a non-issue for ports, since the Erlang caller would just spin up multiple Python interpreters. Does Python allow multiple interpreters per (OS) process, like e.g. Tcl does? If so, then that'd be a possible avenue for mitigating this issue while sticking with NIFs.
- throwawaymaths 2y agoI would have guess the builders of this would have mitigated this problem by running the python in a thread? That won't hang the VM (or cause a segfault at the 1ms boundary). It might cause OS starvation in extreme cases, but you'd have to be really extreme.
- chantepierre 2y agoThere are dedicated "dirty" schedulers for long-running NIFs that by nature cannot be preempted, to avoid interfering with BEAM process scheduling : https://www.erlang.org/doc/apps/runtime_tools/scheduler.html https://www.erlang.org/doc/apps/runtime_tools/scheduler.html
- abrookewood 2y agoThese are excellent points.