Elixir and Rust is a good mix

296 points by rio517 2 years ago | 159 comments
  • mrmincent 2 years ago
    I made the ‘risky’ decision to learn Elixir & Phoenix for a 6 week University project that I delivered earlier this week. It could have turned out to be a terrible decision but honestly once I got my head around it, it’s probably some of the most productive coding sessions I’ve ever had. Deploying to Fly.io was also amazing, I think it was 3-5 commands from signup to deployed with a database. Very happy with it all.
    • MuffinFlavored 2 years ago
      terraform init && terraform apply is 2 commands!

      slightly /s

      kubectl apply is 1 command!

      ansible-playbook is 1 command!

      sh ./do-the-thing.sh

      you get it

    • satvikpendem 2 years ago
      I used to use Elixir, but the lack of static types got to me (especially since I prefer the type-driven development methodology). Using Rust afterwards was great, plus it was faster than the BEAM. I guess, why not use Rust entirely instead of as a FFI into Elixir or other backend language? I've been using Axum and it works pretty well. The only time I had to do FFI with Rust was with Flutter via flutter_rust_bridge, for running a CRDT library (automerge) that was implemented in Rust, for offline app functionality.
      • dxhdr 2 years ago
        > Using Rust afterwards was great, plus it was faster than the BEAM. I guess, why not use Rust entirely instead of as a FFI into Elixir or other backend language?

        Sure, you just need to reimplement light-weight threading with preemptive scheduling prioritizing latency over throughput, extremely robust fault tolerance with a supervision hierarchy, and runtime introspection with code hotloading capabilities. Maybe you could add frictionless distributed system support as well.

        No big deal, why not right?

        • Dowwie 2 years ago
          This list isn't long enough. OTP and Elixir are wonderfully unique. Rust is, however, as well. I don't think that anyone who truly understands the two could comment about using one as a default.
          • lucasyvas 2 years ago
            Is any of this needed though? For some use cases yes, but for many no. It's possible the parent has no practical use for these capabilities.
            • pdpi 2 years ago
              If you're building a project that actually benefits from Elixir/Erlang's features, you can have a constructive discussion about whether those features justify sacrificing static typing. If you don't actually benefit from those features, "I tried to use a language that's unfit for my purposes and found it unfit for my purposes" is a comment of very limited value.
              • 2 years ago
                • hexo 2 years ago
                  [flagged]
                • valenterry 2 years ago
                  I'm not a Rust programmer, but:

                  > light-weight threading with preemptive scheduling prioritizing latency over throughput

                  Rust has Tokio for light-weight threading which might well be sufficient for the majority of use-cases.

                  > extremely robust fault tolerance with a supervision hierarchy

                  One could argue that Rusts compile-time guarantees together with something like the Result-type make it so that such a supervision hierarchy isn't quite necessary and a few "manually implemented" error-boundaries are sufficient. This is also true for errors like network-hickups.

                  > runtime introspection with code hotloading capabilities. Maybe you could add frictionless distributed system support as well

                  Fair enough points.

                  I don't think your attitude against the OP is justified though.

                  • kaba0 2 years ago
                    Come on, Rust has a good type system, but it’s nothing special - Haskell has the same guarantees since decades and it’s not like Haskell programs are suddenly without fault. There is only so much static types can catch without dependent typing, as per Rice’s theorem, types are a ‘trivial property’. Most of the interesting stuff happens at runtime and those are much harder to reason about.

                    Just because you have a Result type doesn’t mean you actually properly/meaningfully handle the error at all, it may just happen that “restart” is the correct solution. Also, Rust is not safe from dead/live locks and many other concurrency issues, only data race free.

                    This is not against Rust, but against the very biased hype for it.

                    • Fire-Dragon-DoL 2 years ago
                      Tokio seems to be a valid replacement for lightweight threading in elixir.

                      Fault tolerance and supervision hierarchy might be unnecessary as mentioned.

                      Hotloading capabilities are unnecessary, most shops go with blue-green deployments, so the hotcode loading is usually unused (and for a good reason, so much complexity!). Distributed computing also goes unnecessary as most applications are deployed with containers with some form of autoscaling, so the industry went a different direction than elixir.

                      That leaves us with runtime introspection, which is pretty cool indeed. But that has to compete with Rust performance.

                      Pretty tough.

                      I love elixir, but when Go got premptive scheduler for goroutine, the need for elixir dropped dramatically. Which is sad because i loved the language and phoenix.

                      I'm hoping it makes a comeback, though!

                    • giancarlostoro 2 years ago
                      Implementing all of that, without using a significant amount of memory for every step of the way to boot. It's impressive how little memory an Erlang process needs.
                      • dist1ll 2 years ago
                        > light-weight threading with preemptive scheduling prioritizing latency over throughput

                        Tasks for backend systems are usually pretty homogeneous. I'm not sure how in such cases the overhead of preemption is in any way better than cooperative multitasking.

                        • calo_star 2 years ago
                          Erlang processes indeed do cooperative multitasking under the hood, something like yielding control to the scheduler roughly every 1000 function calls.
                      • toast0 2 years ago
                        Rust doesn't have a lot of good runtime introspection tools (or they're very not obvious). If you're running a system with a lot of concurrency, it's nice to be able to attach a debugger and find out exactly what's going on with each of your tasks.

                        I haven't seen hot loading for Rust (but a quick search shows there's some out there), and I'm not sure how amenable Rust is to dlopen and friends to force the issue.

                        Erlang (and Elixir) have a constrained language that allows for BEAM to be effectively premptive in a way that a Rust concurrent runtime can't be. At every function call, BEAM checks if the process should be preempted, and because the only way to loop is recursion, a process must call a function in a finite amount of time. A Rust runtime cannot preempt, if you need preemption, you've got to use OS threads, which limits capacity, or you need to accept cooperative task switching.

                        Also, some of us are as anti-typing as you are pro-typing. :)

                        • RhodesianHunter 2 years ago
                          >Also, some of us are as anti-typing as you are pro-typing.

                          Assuming ample experience with both, how does one reach this conclusion?

                          I have yet to see a project of any size that needs to be worked on by multiple teams and is written in an untyped language not descend into dumpster fire.

                          • toast0 2 years ago
                            I work on a lot of 'glue' issues, often with languages like Perl, PHP, and Erlang (and a bit of Javascript here and there). Specifying types all over the place in languages like C, C++, Java, and Rust feels like it gets in the way and limits more than it helps. (feelings more than data here, of course)

                            Sure, at boundaries between teams, you need to specify the data in some way. That could be a type, but for me, often the other team is using a different language than me, so it needs to be a language agnostic type, and it can't include unsigned numbers because Java can't cope, and it can't include large integers because Javascript can't cope, etc. Protobufs are popular, json is too.

                            I have a lot of unpopular opinions though, and that's fine. It's just tiresome that everyone wants to come in and add types to things that don't need them. Also, I agree with dllthomas, most developers and teams are capable of creating dumpster fires in all sorts of environments, with all sorts of tooling. :)

                            • fiddlerwoaroof 2 years ago
                              I tried Haskell for a while and switched to Common Lisp (although I still follow Haskell from a distance). My experience just doesn't match up with the claim that a project of any size written in an untyped inevitably descends into a dumpster fire. I've worked on largish systems in several dynamically-typed languages and several statically-typed and I personally haven't noticed any major difference in overall productivity suggesting that static types are better: they just have different friction points and different ways of working work better in each paradigm.
                              • dllthomas 2 years ago
                                While I'm pretty solidly in the "pro-typing" camp, it seems worth acknowledging that such projects often turn into dumpster fires in typed languages as well, even expressively typed languages.
                                • freedomben 2 years ago
                                  I have yet to see a project of any size that needs to be worked on by multiple teams not descend into dumpster fire. Typing can definitely help, but it's just a small tool. Java is a pretty typed language and I'm seen some real doozy code bases in Java.
                                  • throwawaymaths 2 years ago
                                    >I have yet to see a project of any size that needs to be worked on by multiple teams and is written in an untyped language not descend into dumpster fire.

                                    Github? Dropbox? I mean they both eventually went to type hints in their respective languages or migrated to a typed language but for a long time I'm certain it wasn't.

                                    • ReflectedImage 2 years ago
                                      Untyped languages work fine if you use them with microservices.

                                      The only thing you can't do is have both untyped and monolithic at the same time.

                                    • throwawaymaths 2 years ago
                                      it's certainly possible to write a nif that cooperates with the VM so that its async yield points match up with the VM's expectation of yield points. Definitely tricky to do correctly in C (given that C doesn't have a yield statement, lol, you have to structure it as an awkward tail call where you pickle/unpickle whatever state you want to keep around or unmarshal it from a passed struct). I'm not sure if that's so easy to do in rust.
                                      • toast0 2 years ago
                                        Sure, you can do that for the code you write, but you can't for the code you call. Any Erlang you write or call will have this property, and most of the ERTS provided C code will as well; either it's trivially finite, it is designed to yield during the work, or it's neither but it hasn't triggered anyone to fix it, with occasional deviations of things that become known issues (like -- was for some time; although it got fixed twice and is now fairly ok).
                                      • pjmlp 2 years ago
                                        While Rust doesn't offer this kind of tooling, the JVM and CLR ecosystem certainly do.
                                      • shiryel 2 years ago
                                        As it usually goes in programming, "it depends on your objectives", there are things that are easier accomplished with the ErlangVM than Rust. Also, if you want a language that uses the ErlangVM and has static types, maybe you should take a look at Glean[1].

                                        In my case I prefer to work with Elixir because of the community, as I find easier to work professionally with Elixir than some other mainstream languages, as mostly projects follows the same good practices, use the same tools and have good documentation.

                                        [1] - https://gleam.run/

                                        • aeturnum 2 years ago
                                          I think the most attractive part of the Elixir / Erlang ecosystem is BEAM and its ability to manage code, processes and resources in an extremely parallel environment. Rust operates at a lower level and it feels deceptive to just compare the languages.

                                          That said - if you don't benefit from what BEAM has to offer - I agree Rust is a really attractive alternative.

                                          • vlunkr 2 years ago
                                            I'm in the same boat with Elixir. I love many aspects of the language, but it borrows the fast-and-loose type ecosystem of ruby.

                                            nil is an especially big problem. Any value could be nil, and this will absolutely bite you over and over. nil even allows you to use square brackets for some reason (some_nil_value[:some_key]) which is a great way to disguise the actual issue.

                                            There is optional type checking with Dialyzer, which is good but has some problems. The warning output can be really hard to read, and unless you're diligent in using it across most of your project, it's not very useful, because you'll end up with 'any' values all over.

                                            • OkayPhysicist 2 years ago
                                              I disagree. Any value could be nil, but any value could be 5, too. In fact, nil is just like any other atom. Elixir shines when you lean on pattern matching as much as possible. Your functions should unpack as much as possible within the function definition, frankly the "if" macro is completely superfluous to case and in rare cases cond. You'll find that you match the data you want, and thus reject anything else.

                                              Square bracket dictionary accesses are a code smell, because you should be using %{^key = val} = dict or Map.fetch(map, key) or rarely Map.fetch!(map, key).

                                              If you do that, managing typing in Elixir just boils down to defining structs to differentiate cases where dictionary A and dictionary B contain similar keys but strictly are not interchangeable.

                                              • bPspGiJT8Y 2 years ago
                                                Any errors these techniques could catch would have to happen during runtime, not at compile time, and this is a huge and key difference. In my experience having the type errors being caught at compile time is an incredible boost to productivity. I just write code then hit Ctrl+S and the compiler tells me what I got wrong, instead of all the hassle of having to write functions a line at a time, flip to IEx, run, inspect some intermediary value to make sure it works as intended, while trying to keep so many things in my head at once, which at times can be just overwhelming and highly frustrating.
                                              • throwawaymaths 2 years ago
                                                Elixir is incredibly consistent with how it uses nil. It does take a bit of work -- you have to really pay attention to the convention of fetch! vs fetch vs get -- and often times library maintainers are not... always the best at that... but for all the problems that dynamic languages has, nil in elixir is pretty low on the list. I think I get bitten by maybe one a year, and it causes the sporadic report on sentry, and then I go and fix it, and that's that.

                                                That access acts on nil is unfortunate, but it's necessary for things like get_in.

                                                • vlunkr 2 years ago
                                                  > That access acts on nil is unfortunate, but it's necessary for things like get_in.

                                                  Ah, that explains it at least. Other atoms don't implement the access behavior.

                                              • Dowwie 2 years ago
                                                Concurrency: if you used Elixir, then you should understand how different its concurrency offering is from that of std and tokio. Standardized, structured concurrency makes building complex, highly concurrent, stable, low-latency systems achievable by mere mortals. Rust has no such offering, yet, but it may 5-10 years from now.

                                                Runtime instrospection: the ability to log into a running application to inspect its state, start and stop processes

                                                Compile times: elixir compiles very quickly and has tight feedback loops where as rust compiles slowly and has long feedback loops

                                                Elixir also doesn't get in the way that the borrow checker does, allowing a programmer to just get on with work and not become saddled with related debugging.

                                                Ecosystem: data pipeline processing via GenStage, Broadway, Flow -- wow. Rust developers should take note of what can be achieved in Elixir. However, Rayon and crossbeam are fantastic. Elixir cannot compete with Rust on performance in the category of pipeline processing, but it has very high marks in other very important categories that need to be considered for professional development.

                                                I don't think that fault isolation is as compelling an advantage over Rust as it is other languages. Rust makes defense programming a regular part of development, unwrap used sparingly. Faults hardly happen because the program isn't designed to crash. In the rare event that a well-designed Rust program crashes, it's probably managed by an orchestrator that will restart. Both well-designed Elixir and well-designed Rust applications can enjoy very long uptimes if that is a goal.

                                                This is just a starting point of discussion.

                                                • malkosta 2 years ago
                                                  > why not use Rust entirely instead of as a FFI into Elixir

                                                  Because many times you value fault-tolerance and distribution more than performance.

                                                  • rapnie 2 years ago
                                                    There's a couple of Rust libs and frameworks inspired on Erlang/OTP in 'best of both worlds' attempts, such as https://lunatic.solutions

                                                    I found others like Lunatic before, but cannot remember right now.

                                                    • malkosta 2 years ago
                                                      I guarantee your life will be simpler with Erlang.
                                                  • hosh 2 years ago
                                                    Everyone have different needs. For us, OTP on top of pre-emptive lightweight threads and a REPL in production is a huge winner for troubleshooting, finding performance bottlenecks, and reliability.
                                                    • kerkeslager 2 years ago
                                                      > I used to use Elixir, but the lack of static types got to me (especially since I prefer the type-driven development methodology).

                                                      You might be interested in Gleam[1].

                                                      [1] https://gleam.run/

                                                      • satvikpendem 2 years ago
                                                        I used to use some Gleam back before the syntax change, when it looked more like Haskell. I found that rather than using a language with a comparatively smaller community, I'd rather just use something that's well supported, so I settled on Rust instead.
                                                      • callamdelaney 2 years ago
                                                        Rust is faster, but that's like saying writing to memory is faster than writing to a database - sure, it is - but the BEAM is designed with something completely different in mind and is still 'quick enough' for handling those cases. That said dealing with elixir syntax is like rubbing sandpaper in your eyes for 8 hours a day.
                                                        • 2 years ago
                                                        • zinclozenge 2 years ago
                                                          I pretty much agree with this. As much as I liked the core language, I absolutely hated using anything else in the ecosystem, and in my opinion a lot of that was due to lack of static types. Using Ecto as the main way of interacting with the database was a miserable experience. Part of that was due to skill issue I'm sure.
                                                          • kerkeslager 2 years ago
                                                            > As much as I liked the core language, I absolutely hated using anything else in the ecosystem, and in my opinion a lot of that was due to lack of static types.

                                                            You might be interested in Gleam[1].

                                                            [1] https://gleam.run/

                                                            • parthdesai 2 years ago
                                                              > Using Ecto as the main way of interacting with the database was a miserable experience.

                                                              Ecto is prolly one of the best ways to interact with db. Genuinely curious, what other ORMs have you used?

                                                              • zinclozenge 2 years ago
                                                                I usually just write the SQL, honestly. For the last 6ish years of my career I've mainly been in the Golang world, so the closest I've gotten to an ORM is a utility to scan rows into structs.
                                                              • sergiotapia 2 years ago
                                                                On the flipside for me Ecto is the perfect balance between ORM and writing raw SQL queries. I've used many different ORMs like Entity Framework, ActiveRecord, Prisma, Ecto - Ecto stands alone.
                                                                • throwawaymaths 2 years ago
                                                                  I believe... technically Ecto is not an ORM but a query builder, which may be why you like it better than an ORM.
                                                                • malkosta 2 years ago
                                                                  > Ecto as the main way of interacting with the database was a miserable experience

                                                                  That's very common when you don't know DBs. But DB savy developers usually claim the opposite, because the syntax is more familiar.

                                                                  • zinclozenge 2 years ago
                                                                    > That's very common when you don't know DBs.

                                                                    I'm not a 20 year DBA greybeard veteran, but I'm comfortable enough to write schemas and queries by hand without any issue, and the entire time I wished I could do just that instead of using Ecto.

                                                                • dsiegel2275 2 years ago
                                                                  I used to use Elixir. I still do, but I used to too.
                                                                  • whalesalad 2 years ago
                                                                    There is a great talk from the creator of the language on why static types are not necessary. Of course there is nothing wrong with wanting them and feeling that they are, but I think he (José) makes really great arguments for why Elixir is the way it is. https://www.youtube.com/watch?v=Jf5Hsa1KOc8
                                                                    • baby 2 years ago
                                                                      I’m with you there. I haven’t played with Elixir but did some Erlang. I really liked Erlang. Worked a bit in Whatsapp’s codebase as well. But IMO it’s def. not as nice as Rust + using a single language is better.
                                                                      • eikenberry 2 years ago
                                                                        Could also be nice as it means the Rust parts are smaller and so its slow compiler speed isn't as much of a bother. You get the safety and runtime speed of Rust with the fast iteration cycle of Elixir. Best of both worlds type of thing.
                                                                        • satvikpendem 2 years ago
                                                                          I often find FFI more annoying than it should be (ie something always breaks somewhere), I try to avoid it where possible.
                                                                        • peoplefromibiza 2 years ago
                                                                          > I guess, why not use Rust entirely instead of as a FFI into Elixir or other backend language?

                                                                          Because Rust brings none of the benefits of the BEAM ecosystem to the table.

                                                                          I was an early Elixir adopter, not working currently as an Elixir developer, but I have deployed one of the largest Elixir applications for a private company in my country.

                                                                          I know it has limits, but the language itself is only a small part of the whole.

                                                                          Take ML, Jose Valim and Sean Moriarity have studied the problem, made a plan to tackle it and started solving it piece by piece [1] in a tightly integrated manner, it feels natural, as if Elixir always had those capabilities in a way that no other language does and to put the icing on the cake the community released Livebook [2] to interactively explore code and use the new tools in the simplest way possible, something that Python notebooks only dream of being capable of, after a decade of progress

                                                                          But they do not not stop there, the documentation is always of very high quality, even for stuff not coming from the core developers, and they also regularly release educational material that is worth a hundred times a gain in speed.

                                                                          They've set a very high quality standard and I noticed how much it is important only when I stopped programming daily in Elixir and went back to other more hyped or establish ecosystems.

                                                                          That's not to say that Elixir is superior as a language, but that the ecosystem is flourishing and the community is able to extract the 100% of the benefits from the tools and create new marvellously crafted ones, that push the limits forward every time, in such a simple manner, that it looks like magic.

                                                                          Going back to Rust, you can write Rust if you need speed or for whatever reason you feel it's the right tool for the job, it's totally integrated [3][4], again in a way that many other languages can only dream of, and it's in fact the reason I've learned Rust in the first place.

                                                                          I must also say that the work done by the Rust community looks refreshing as well, if you look at the way rustler works it was very well thought and made writing NIFs, something that seemed arcane and distant, only for the proverbial mad professor to try, a breeze. Kudos to them.

                                                                          But the opposite IMO is not true, if you write Rust, you write Rust, and that's it. You can't take advantage of the many features the BEAM offers, OTP, hot code reloading, full inspection of running systems, distribution, scalability, fault tolerance, soft real time etc. etc. etc.

                                                                          But of course if you don't see any advantage in them, it means you probably don't need them (one other option is that you still don't know you want them :] ). In that case Rust is as good as any other language, but for a backend, even though I gently despise it, Java (or Kotlin) might be a better option.

                                                                          [1] https://github.com/elixir-nx/nxhttps://github.com/elixir-nx/axon

                                                                          [2] https://livebook.dev/

                                                                          [3] https://github.com/rusterlium/rustler

                                                                          [4] https://dashbit.co/blog/rustler-precompiled

                                                                          • victorbjorklund 2 years ago
                                                                            Have you checked out Gleam? It is statically typed running on BEAM
                                                                          • bglusman 2 years ago
                                                                            I admit for a long time this was my primary motivation to learn Rust, but, sadly, I haven't come across problems in years that were CPU bound/where I needed something like Rust... Rustler still looks like a great fit if needed, but, depending on the use case, if I were CPU bound and needed to write my own code/not just use a Rust library, I'd be as or more likely to look at using Zig and Zigler[0], for much faster learning curve, and from what I've read, easier tighter integration into elixir, including I think language server integration. Some discussion here[1] though I forget if I listened to this one or not.

                                                                            [0]https://github.com/ityonemo/zigler [1]https://podcast.thinkingelixir.com/83

                                                                            • packetlost 2 years ago
                                                                              Efficient PLs are useful for latency sensitive applications too, not just CPU bound ones. Though, that doesn't widen the number of cases by much...
                                                                              • jimbob45 2 years ago
                                                                                I haven't come across problems in years that were CPU bound/where I needed something like Rust

                                                                                This is where Rust falls short of C#: scaling to the issue at hand. C# can build you a beautiful app at a high-level but also lets you dick with pointers and assembly at a low level. Rust insists on defaulting to pass-by-move and an arcane trait system that hold it back from being usable in large projects.

                                                                                • zelphirkalt 2 years ago
                                                                                  If Rust had gone for a traditional OOP system, the "everything must be OOP/use inheritance everywhere" crew would have messed up the ecosystem pretty quickly. The traits concept is refreshing and traits + structs encourage composition over inheritance. I think it has been a huge plus for the language and the ecosystem.
                                                                                  • kaba0 2 years ago
                                                                                    I really like the trait system, but “refreshing” might not be the correct word given that it is pretty much what Haskell had for I don’t even know how many years.
                                                                                  • shrimp_emoji 2 years ago
                                                                                    Composition is objectively superior to inheritance, and that's all traits are: https://en.m.wikipedia.org/wiki/Composition_over_inheritance
                                                                                • peregrine 2 years ago
                                                                                  Author here. The rustler team has really made a great developer experience using Rust in Elixir and I just had to share.
                                                                                  • maciejgryka 2 years ago
                                                                                    Shameless plug & also a point of support for the article: I used that same stack to build https://regex.help/ (more details here https://maciej.gryka.net/building-regex-help)
                                                                                  • Dowwie 2 years ago
                                                                                    If you're extending Elixir with Rust, familiarize yourself with precompiled rustler: https://dashbit.co/blog/rustler-precompiled
                                                                                    • brightball 2 years ago
                                                                                      This is essentially what has gotten me into Rust. Knowing how easy Elixir makes it to offload a piece of my system that would benefit from it without needing to write the entire thing in Rust.
                                                                                      • pawelduda 2 years ago
                                                                                        I used this in past, but there were a lot of people saying that NIFs/ports are dangerous to use. Did any of this change or are there tried and tested practices that can be applied? What I used this for wasn't critical app so I didn't mind it going down.

                                                                                        IIRC one threat was Rust sharing memory with BEAM which could exhaust it and cause OOM crash?

                                                                                        • toast0 2 years ago
                                                                                          I wouldn't use the word dangerous. NIFs and (linked in) ports are a risk, yes. You're loading native code, and you don't have intrinsic protection against it doing naughty things that alter the underlying shared state in unapproved ways. Things like writing to improper addresses, closing improper filehandles, opening filehandles and leaking them, leaking memory, other improper use of syscalls, or just running for too long.

                                                                                          The only proper solution is to audit and understand the code you're running, but hoping it's fine often works too; maybe formal methods, but to a first approximation, nobody uses those. Did you audit all of ERTS and/or OTP? I'm guessing probably not, but it's there to review if you run into a problem.

                                                                                          IMHO, it's not worrying about if BEAM will crash; worry about it not crashing instead. If your Rust NIF ties up a scheduler with an infinite loop, that has the potentially to lock up the whole BEAM once another scheduler needs to do something that requires full cross scheduler coordination.

                                                                                          BEAM can certainly crash on OOM; although I recommend setting a ulimit to ensure it will, because when it crashes, you can recover. I've also run into situations where instead of crashing or being killed by the OS OOM killer (which is close enough to crashing), the OS gets into some tricky to debug state where your application is neither functioning nor killed. Sometimes, you even get into a state where BEAM is making progress, but very slowly; that's a fate worse than death.

                                                                                          If you follow the Erlang philosophy, you'll have a recovery strategy from crashes or other deaths. Heart can be used to turn completely blocked into death, although I never used it professionally. But you've still got to worry about working but not well.

                                                                                          • peregrine 2 years ago
                                                                                            Nif's are always a risk to deploy cause they run embedded in the Beam runtime. That said we all use many nif's to do all kinds of stuff and its fine.

                                                                                            I go into this in the article a little but but the ruslter team has made dirtycpu and dirtyio macro's to help reduce the risks.

                                                                                            • OkayPhysicist 2 years ago
                                                                                              NIFs are dangerous, ports are not. Basically a NIF runs in the same memory space as the BEAM, so misbehavior of the NIF can crash the entire application. On the other hand, they have wildly better performance than ports, which have to operate through STDI/O.

                                                                                              The Rust / BEAM memory sharing problem does exist, but it's not nearly as bad as in more traditional C NIFs, because almost all C programs leak memory due to bad manual memory management. Hence all the buzz about Elixir+Rust.

                                                                                              • toast0 2 years ago
                                                                                                I personally include port drivers in with ports, which gives you the same level of shared runtime environment as NIFs, with the benefits and drawbacks. Sometimes it makes more sense to interface as a port rather than as a function. Of course, sometimes it makes more sense to interface as a C-node rather than either; then you can be on a totally isolated machine, and the C-node can even crash or otherwise disable the whole OS and your Erlang node will be fine.
                                                                                                • OkayPhysicist 2 years ago
                                                                                                  What's the benefit of a port driver over a NIF?
                                                                                                • throwawaymaths 2 years ago
                                                                                                  ports can be dangerous! One time my server lost performance because of a lot of zombie processes.
                                                                                              • mtremsal 2 years ago
                                                                                                This is amazing! The Gods have sent me the perfect excuse to finally read all the rust ebooks I’ve been collecting! I have a toy project where certain functions are CPU-bound and would make for a perfect learning project. Very timely read!
                                                                                                • victorbjorklund 2 years ago
                                                                                                  Elixir, rust and fly.io in the same post. HN bingo.
                                                                                                  • FpUser 2 years ago
                                                                                                    I remember MS reps coming to our offices in the 90s and "Teaching" us how to program and how it is cool to have one person writing GUI and the other "guru" writing high performance C++ to be used in critical parts.

                                                                                                    I showed them a piece of software that was mighty fast, Internet enabled and GUI intensive. They liked the software but asked where did you get this particular screen control from. You've got to see their faces when told that the whole software was written by a single person in Delphi.

                                                                                                    • alberth 2 years ago
                                                                                                      Doesn't the use of Rust have to be extremely minimal because ErlangVM has hard time limits on their preemptive scheduler and if your Rust code hasn't finished when preempted, that causes lots of problems.

                                                                                                      EDIT: thanks for pointing out where in the article this is talked about.

                                                                                                      • peregrine 2 years ago
                                                                                                        I go into this in the article :) the rustler team has a made a DirtyNif that can work around that, or you can manually yield if you'd like.
                                                                                                        • clessg 2 years ago
                                                                                                          Indeed, your (excellent) article addresses this, here's the gist for those following along:

                                                                                                          > Change `#[rustler::nif]` to `#[rustler::nif(schedule = "DirtyCpu")]`

                                                                                                          > This tells the Rustler and BEAM to automagically schedule this in a way that won't block the entire world while it works. Again amazing, this is called a DirtyNif and is way more difficult to work with when you are manually using this via C.

                                                                                                          Essentially, regular NIFs have to be extremely fast (< 1ms) because the VM can't preempt them - they run on the same scheduler threads the BEAM itself uses. Dirty NIFs solve this by running jobs in a completely separate thread pool ("dirty schedulers"). Rustler's docs explain it succinctly (https://docs.rs/rustler/latest/rustler/attr.nif.html):

                                                                                                          > For functions that may take some time to return - let’s say more than 1 millisecond - it is recommended to use the `schedule` flag. This tells the BEAM to allocate that NIF call to a special scheduler. These special schedulers are called “dirty” schedulers.

                                                                                                          > We can have two types of “lengthy work” functions: those that are CPU intensive and those that are IO intensive. They should be flagged with “DirtyCpu” and “DirtyIo”, respectively.

                                                                                                          (Somewhat OT, but since I'm here: excellent article @ peregrine! I really enjoyed the read. Elixir and Rust are such a perfect fit. Plus, some of the specifics will be helpful for certain image-related things I'm actively working on, which is always nice. :) )

                                                                                                      • benatkin 2 years ago
                                                                                                        This makes me think of DotCloud, the precursor to Docker Inc. The idea that you can run anything has been around for a while. I don't see how Fly.io is especially good for Elixir. It seems to be about delivering reliable resources for each service, and it works no matter the web framework, as long as you have a PaaS that's geared towards running arbitrary OCI images. These posts seem a lot like the content on the DigitalOcean site, except they're written by staffers instead of freelancers. https://www.digitalocean.com/community/pages/write-for-digit...
                                                                                                        • peregrine 2 years ago
                                                                                                          One of the interesting bits about Fly is we have our own servers distributed globally and connected via wireguard. Which makes it is trivial to setup Elixir/Erlang distribution globally, and most importantly, close to your customers. While its not a magic bullet it is pretty amazing to type a few commands have a globally deployed and directly distributed application.

                                                                                                          Further Fly builds its Dashboard internally with Phoenix LiveView. We want the Phoenix and Ruby and Laravel and more communities soon, to grow because we believe if they grow, we will too.

                                                                                                          • benatkin 2 years ago
                                                                                                            That's just internal networking isn't it? VPC on the big clouds, DigitalOcean, and Vultr. Private Services on render.com.

                                                                                                            I remember when Fly.io used to tout Firecracker but that is just a KVM engine, along with QEMU used on a zillion hosts.

                                                                                                            What I'd like to see are customer success stories.

                                                                                                            Edit: looks like you have to set up your own cross-region links on DigitalOcean and Vultr. So that interests me somewhat. :)

                                                                                                          • mrdoops 2 years ago
                                                                                                            Elixir does distribution and concurrency really well, so Fly making multi-region deployments easy makes it a good fit.
                                                                                                            • benatkin 2 years ago
                                                                                                              So is Fly good for small sites or is it good for huge sites? What's a big customer? For small sites my idea of a multi-region deployment is a single-region deployment that works in multiple regions thanks to the magic of the Internet.

                                                                                                              I see this which mostly seems to be content sites. https://www.wappalyzer.com/technologies/paas/fly-io/ Same on the first forum result: https://community.fly.io/t/customer-success-stories/4882

                                                                                                              • mrdoops 2 years ago
                                                                                                                I'd say its good for both.

                                                                                                                Small sites because it is low-bandwidth to figure out how to use. Time is money and if I'm just tinkering on the weekend I don't really want to learn Kubernetes or the labyrinth that is AWS; I just want to ship an app.

                                                                                                                Big sites because your users get routed to the node closest to them and again you can do this without a lot of time investment. For Elixir I just wire up Libcluster and my nodes can talk to each other.

                                                                                                                I really want GPUs on Fly soon though. Just take my money Kurt. (I hear they're working on it)

                                                                                                          • waynesonfire 2 years ago
                                                                                                            IO in erlang is slow, is Rustler a good candidate to improve IO intensive applications?
                                                                                                            • throwawaymaths 2 years ago
                                                                                                              Where did you get this idea? Depending on your task, Erlang can be very fast with IO because it will aggressively use writev/readv instead of write. Obviously, you can do this if you "know about it" in low level langs, but it might be a pain.

                                                                                                              Erlang is generally considered to be compute-slow (which is generally the case without dropping to nifs).

                                                                                                              • toast0 2 years ago
                                                                                                                Is IO in Erlang slow? I never got that impression; iolists are a good fit for scatter/gather I/O, which reduces copying, and Erlang supports kqueue, epoll, and I believe similar on Windows. I don't follow Erlang on Linux closely, so I don't know if there's any movement towards io_uring, which does seem promising to help with throughput.

                                                                                                                Either way, I wouldn't expect doing I/O in NIFs or ports to substantially improve throughput, unless you're also moving significant processing into that layer as well, or you're going to end up doing substantially the same level of marshaling work as ERTS does, just in a different language. Setting up a different set of kqueue/epoll descriptors sounds like a lot of work for not much gain too, IMHO; again, maybe io_uring would be useful, but I think you'd be better served to bite the bullet and integrate it with ERTS.

                                                                                                                • sph 2 years ago
                                                                                                                  IO in Erlang is faster than many other managed languages, thanks to stuff like IO Lists that are dealt with very efficiently through iovec syscalls (readv, writev).

                                                                                                                  Then add efficient pattern matching for binary data, and networked servers on the BEAM are the most ergonomic than any other language.

                                                                                                                  All this stuff that the BEAM offers you out of the box can be replicated in any native language with a lot of boilerplate and ceremony.

                                                                                                                  Your "Hello, <name>" webapp in Rust will probably need two allocations and a string concatenation, while on the BEAM, if constructed as an iolist, it's a single writev syscall, using a static "Hello, " string and a shared "<name>" reference from the parsed HTTP data.

                                                                                                                  • weatherlight 2 years ago
                                                                                                                    I/O in beam languages is considered pretty fast, are you sure you aren't mistaken?