Elixir 1.19

354 pointsposted a day ago
by theanirudh

113 Comments

crbelaus

a day ago

The progressive introduction of automated type checking in Elixir should serve as a reference on how to improve a programming language gracefully without breaking changes.

So many examples of programming languages have huge breaking changes between versions that end up creating a split in the ecosystem that takes years to resolve.

Thankfully José has been very clear about Elixir being done since at least 2018. The language is stable and the language/core foundation is not changing anymore.

https://www.youtube.com/watch?v=suOzNeMJXl0

Truly outsanding work and stewardship.

bryanlarsen

17 hours ago

> So many examples of programming languages have huge breaking changes between versions

I can only think of 2: python 3 and perl 6.

Those two were very traumatic so it's not surprising it feels like more.

pjmlp

10 hours ago

.NET Framework => .NET

While C#, F#, VB and C++/CLI were kept compatible, it doesn't help when the library stuff you want to call isnt' there any longer.

C++ removal of exception specifiers, GC API,

C VLAs got dropped in C11, function prototypes changed meaning in C23, and K&R declarations were dropped from the standard.

Java, already someone else mentioned.

D, the whole D1 => D2 transition, and Tango vs Phobos drama.

lelanthran

3 hours ago

> C VLAs got dropped in C11, function prototypes changed meaning in C23, and K&R declarations were dropped from the standard.

Not so sure I'd call these huge breaking changes. They're breaking, sure, but I'd expect them to be trivial to fix in any existing codebase.

Maybe VLAs are a huge breaking change? Most code never used it due to no way at all to use them safely, so while it is a pain to replace all occurrences of them, the number of usages should be really low.

pjmlp

3 hours ago

It was breaking enough for the Linux kernel, and the money Google sponsored to the effort remove them,

https://www.phoronix.com/news/Linux-Kills-The-VLA

Breaking changes are breaking changes, even if it is only fixing a comma, someone has to spend part of their time making it compile again, which many times maps to actual money for people working at a company, their salary mapped into hours.

lelanthran

2 hours ago

> Breaking changes are breaking changes,

No disagreement there, but the context ITT was specifically about huge breaking changes. I consider those breaking changes, but not necessarily huge ones.

aw1621107

8 hours ago

> GC API,

Bit of a quibble but I'm not sure I'd call that a "huge breaking change" given that that feature wasn't really implemented in the first place, let alone actually used.

pjmlp

8 hours ago

It could have been implemented privately by someone, but yeah I kind of agree.

https://cppreference.com/w/cpp/compiler_support/11.html

It was a bad feature, as the two main C++ commercial products that make use of GC, namely C++/CLI and Unreal C++, were never taken into account while designing it, a good example how WG21 often does PDF driven design.

isodev

13 hours ago

Swift! Multiple times introduced fundamental changes that were not only breaking but required semantic rewrites of code. The most recent breakage being Swift’s latest version adding concurrency features.

agrounds

13 hours ago

Scala 3 has failed to be widely adopted, and now the language as a whole is more or less dead. Not that that’s due to the 2-to-3 transition entirely.

pjmlp

8 hours ago

That happens to all guest languages unless that have unique selling points that make that differention happen and take off on their own, as genesis for their own platform.

See all platforms that have their identity tied with a specific language, the platform's language always has a guaranteed future as long as the platform continues to be industry relevant.

The others on top, come and go.

patates

9 hours ago

I haven't been following, now I'm sad to hear... Scala is really dead? What'd be the JVM alternative?

codr7

8 hours ago

Don't know about Scala alternative, but the language I've found most enjoyable on the JVM is definitely Kotlin.

orthoxerox

8 hours ago

It's not dead dead, but no new projects are choosing it. Those that chose Scala as the better Java can now just use the better Java from the latest JDK.

spooky_deep

7 hours ago

Scala still offers lots over modern Java.

The issue for me is that Scala design lacks focus. They say yes to too many features.

dustingetz

6 hours ago

the OO/FP fusion hypothesis resulted in a complicated language on the OO side (too complicated for enterprise application layer) and on the FP side an autistic culture war at the seam between FP frameworks. Functional Scala remains world class at high reliability services such as video streaming at Disney+ and Comcast, and Amazon search but not so much the Java everyman use case that I recall it being marketed for 15 years ago. And now the Scala leadership and the industry frameworks are pulling in different directions, Scala is academically funded.

jameslars

14 hours ago

Java 8 -> anything 11+ wasn't great at scale. It's been smooth sailing for a long time again though.

patates

9 hours ago

What started as a "this should be just a few namespace changes" might have cost thousands of person days in my current job. So many tests red, the whole CI/CD broken, and when all "fixed" and done, there were still some uncaught production bugs haunting us for many months... Simply horrible.

On paper, it really was just a few changes. In practice, it forced a massive transitive dependency and technical debt cleanup for many companies.

pandemic_region

9 hours ago

I don't remember anything significantly bumpy for about 30 large-ish applications we migrated from 8 to 11, guess the mileage varied. JDK is serious stable stuff.

ffsm8

14 hours ago

Uh, wasn't the only breaking change a rename/changed path in some standard lib path?

pjmlp

10 hours ago

Introduction of modules, closing down of APIs that no one should be using, since Java 9 deprecated methods actually get removed a few versions later.

Also breaking changes do happen, see list of removed methods

https://docs.oracle.com/en/java/javase/17/migrate/removed-ap...

cesarb

3 hours ago

On top of that, there was the removal of built-in J2EE; you needed to add external copies of the J2EE pieces, and some of them (like CORBA) weren't available as separate packages. And later versions of these external J2EE packages changed the namespace of all their classes, which is especially painful in Java due to its common use of dynamic loading of classes by name and lazy linking (and lazy linking errors do not inherit from Exception, which allows them to escape from catch-all "catch (Exception e)" clauses). The rest of the ecosystem is starting to depend on these new versions, so staying with old versions of these J2EE packages is not an option.

cess11

10 hours ago

Deprecations, which also affects libraries, i.e. the dusty one you were chugging along on top of might need to be replaced or adopted because the original maintainer gave up years ago.

nasmorn

7 hours ago

Ruby 1.8 to 2.0 with a layover in 1.9 was also not fun. Compared to python it is chicken shit of course

tobyhinloopen

8 hours ago

NodeJS with its commonjs vs modules, it's a huge mess IMO. Add typescript and build systems for bonus clusterf*cks.

agos

2 hours ago

the whole commonjs vs modules is there precisely because they chose not to introduce a breaking change.

Typescript has introduced breaking changes but they're not that bad

chillfox

15 hours ago

Ruby 1.8.7 -> 1.9.0 caused a lot of problems in the sysadmin space that took years to get resolved.

tyre

15 hours ago

triggered.

There was a rails upgrade around that time that was similarly painful, at least in the humongous rails app I was working in.

andruby

9 hours ago

Rails 1 to 2 was a big one indeed. Rails 2 introduced restful routes iirc, which meant refactoring a lot of fat controllers.

thibaut_barrere

10 hours ago

Ruby 1.8 to 1.9 has been fairly hard, despite being a minor version change. Strings became Unicode, this broke tons of stuff. Also hash ordering.

This caused quite a lot of work on the apps I worked on.

dragonwriter

10 hours ago

> Ruby 1.8 to 1.9 has been fairly hard, despite being a minor version change.

Ruby 1.8 to 1.9 was a major version change in the semver sense; Ruby wasn't using Semver before, IIRC, 2.1.0, it was using a scheme that was basically loosely like Semver with an extra prefix number. Ruby minor versions were equivalent semver major (and also had a less-stable implication for odd numbers, more stable for even, Ruby “tiny” versions were equivalent to semver minor, and Ruby still had patch versions.

throwawaymaths

12 hours ago

ruby 1.8 -> 2 was miserable, especially as the linux distros kept 1.8 and gems started requiring 2.

dgfitz

17 hours ago

C++11 fucking with strings also comes to mind.

Blackthorn

13 minutes ago

C++20 too but luckily basically every compiler has added a switch to turn that insanity off, saving the language.

pjmlp

8 hours ago

That was more a GCC problem, but yeah.

jen20

14 hours ago

Although it was somewhat optional, adding async to Rust had a similar feel. Also various Swift versions had source breaking changes back in the day.

codr7

8 hours ago

I gave up on Swift when the next release suddenly required actor annotations all over the place. They've been making weird decisions lately.

TomBers

a day ago

Completely agree. I feel no pressure to constantly upgrade my Elixir versions. I just look at the changes and there is often useful features that make me want to upgrade, as opposed the feeling of dread when I am pushed to upgrade.

atonse

a day ago

Yea each time I’ve upgraded (been using elixir in production since 2017), it’s gone way smoother than I had anticipated.

I usually find the Erlang/OTP upgrades to be a bit more problematic compatibility-wise.

So I’m often in the latest elixir but one Erlang/OTP version behind cuz I wait a few months for all the kinks to be worked out.

flux293m

3 hours ago

That's definitely the right way to do it, I've had exactly the same experience.

Elixir still seems kind of rough and missing creature comforts, needs stabilization and guides to accomplish goals. There's a lot of broken/under-maintained packages and unhelpful guides that don't work because there's so much Phoenix ecosystem churn. It could get better but all the things™ need curation and approachable documentation. Not everyone wants LiveViews or to use their component system, and the learning curve for compatibility with other tools and technologies is still way too steep.

Python 3 was really, really needed to fix things in 2. Hence 2 became 3. They managed it pretty well, vaguely similar to Go, with automated update tools and compatibility-ish layers. It had its speed bumps and breakages as not everything went smoothly.

OTOH: Ruby 3 went the wrong way with types separate files and fragmentation of tools. And that's not mention having to opt-in with boilerplate to change how String literals work. Or: gem signing exists but is optional, not centrally-managed, and little-used. Or: Ruby Central people effectively stole some gems because Shopify said so. PS: many years ago Hiroshi Shibata blocked me from all GH Ruby contributions for asking a clarifying question in an issue for no reason. It seemed agro, unwarranted, and abrupt. So the rubygems repository fragment drama seems like the natural conclusion of unchecked power abuse lacking decorum and fairness, so I don't bother with Ruby much these days because Rust, TS, and more exist. When any individual or group believe they're better than everyone else, conflict is almost certainly inevitable. No matter how "good" a platform is, bad governance with unchecked conduct will torpedo it. PSA: Seek curious, cooperative, and professional folks with mature conflict-resolution skills.

It's a good idea™ to think deeply and carefully and experiment with language tool design in the real world before inflicting permanent, terrible choices rather than net better but temporarily-painful ones. PSA: Please be honest, thoughtful, clear, and communicate changes in advance so they can be avoided or minimized to inflict least net pain for all users for all time.

Honestly, I hope more development goes into making Phoenix/Elixir/OTP easier, more complete, more expressive, more productive, more testable, and more performant to the point that it's a safe and usable choice for students, hobbyists, startups, megacorps, and anyone else doing web, non-web, big data, and/or AI stuff.

Plug for https://livebook.dev, an app that brings Elixir workbooks to a desktop near you. And https://exercism.org/tracks/elixir

_susanoo

a day ago

> PSA: Please be honest, thoughtful, clear, and communicate changes in advance so they can be avoided or minimized to inflict least net pain for all users for all time.

> Honestly, I hope more development goes into making Phoenix/Elixir/OTP easier, more complete, more expressive, more productive, more testable, and more performant to the point that it's a safe and usable choice for students, hobbyists, startups, megacorps, and anyone else doing web, non-web, big data, and/or AI stuff.

Seriously, this has been the case all the time. It's a great fit for AI, web (Phoenix), non-web (Nerves), students (Pragstudio), hobbyists (hi), megacorps (Discord, bleachereport).

What do you mean it's not testable, productive, expressive enough? Do you mean the entire elixir community is just fiddling about with unsafe software?

This comment seems just like a giant ragebait.

abrookewood

15 hours ago

"Python 3 was really, really needed to fix things in 2. Hence 2 became 3. They managed it pretty well".

I don't know how you can say this honestly - it was turbulent and fraught with trouble and angst. It most certainly was NOT handled well.

dmix

17 hours ago

Our transition to Ruby 3 was very smooth. The actual PR was pretty tiny considering how big our project was. Nothing like the crap I dealt with in JS framework upgrades for much smaller projects. Even Rails is pretty easy to upgrade these days, both are very mature.

bmitc

a day ago

The Phoenix churn is definitely real. It's so much so that I've never gotten into it. It's also extremely macro heavy, and so it's its own DSL or collection of DSLs. A concrete example of the churn is that the LiveView book has been "about to release" for five years now.

Although, what parts of Elixir itself are rough or missing creature comforts? I generally feel it's stable and fine, but I admittedly haven't written Elixir code in a couple of years, sadly.

josevalim

18 hours ago

LiveView was still before v1.0, hence the churn, but Phoenix itself did not introduce breaking changes since v1.0, released more than a decade ago. Our skeleton for new applications change, as best practices around web apps are still evolving, but it is completely up to you to migrate. As a reference point, most other web frameworks have gone through several major versions in the same time, changing how new apps are built but also breaking old ones.

The idea that Phoenix is also mostly macros does not hold in practice. Last time this came up, I believe less than 5% of Phoenix' public API turned out to be macros. You get this impression because the initial skeleton it generates has the endpoint and the router, which are macro heavy, but once you start writing the actual application logic, your context, your controllers, and templates are all regular functions.

dnautics

12 hours ago

> The idea that Phoenix is also mostly macros does not hold in practice

no, but the Framework does push you into using them. A good example is the `use MyAppWeb` pattern. That's a macro that nests other macros. the good news is that you can pretty much excise that and everything works fine, and LLMs have no problem even! (i think they slightly prefer it)

a few cognitive pain points with phoenix macros:

plug: (love it dearly) but a bit confusing that it creates a conn variable out of whole cloth. a minor complaint. worth it, otherwise.

phoenix.router: is a plug but isnt quite a plug.

anyways that's it! the rest is ~fabulous. i think to find a framework where you have two minor complaints is a blessing. remember how activerecord automagically pluralized tables for you?

oxidant

an hour ago

What do you mean, "creates a Conn variable out of whole cloth"?

Conn is just a pipeline of functions, the initial Conn struct is created at request time and passed through to each function in the pipeline.

ch4s3

14 hours ago

“It’s all regular functions” should be on our collective bumper sticker.

bmitc

2 hours ago

Thanks for the clarifications!

> I believe less than 5% of Phoenix' public API turned out to be macros.

The idea may still be right, but I'm curious if that addresses the majority of the public API that users are greeted with. I have unfortunately not written Elixir in a few years (cries), and I've never fully grokked Phoenix, so perhaps I'm still wrong.

giraffe_lady

a day ago

> It's also extremely macro heavy, and so it's its own DSL or collection of DSLs.

I mean this describes every full stack web framework right? Like sure if the underlying language doesn't have macros or macro-like tools that limits how perverted the syntax can get but the line between "DSL" and "API" gets really blurry in all of these massive frameworks.

bmitc

a day ago

That's true for languages that have macros. I just don't like macros, as they get over-abused in every language that has them. I'd much rather deal with just boilerplate and tedious syntax but still straightforward and completely in the language over macros, for the most part. Some macros are indeed useful, like in Rust with `println`, but they still get thrown everywhere.

giraffe_lady

a day ago

Frameworks in langauges that don't use macros have this problem too that's what I was getting at with the DSL vs API thing. I don't want to litigate the worthiness of macros for a given purpose here. But if you don't use them for this you have to use something for this the problem doesn't go away.

Wherever rails or phoenix has macro-defined syntax to handle a specific task, laravel or whatever will have a collection of related functions that need to be used in very specific ways to accomplish the same thing. Whether this collection is a "class" with an "api" or whether it is a "language" defined around this "domain" you will have the abstraction and the complexity.

Having a preference for one approach of managing this abstraction & complexity seems fine but "a collection of DSLs" is pretty much what a web framework is so that can't be the problem here.

zdragnar

14 hours ago

It really depends on how good your inspecting tools are. Using runtime methods and functions instead of macros mean the code is all right there, and what you're debugging is what you see in your editor (setting aside silly things like reflection shenanigans).

With macros, even language servers may need customization if they introduce new syntax. The code that runs doesn't exist until it runs, so you can't see it ahead of time.

This doesn't sound like too big a problem if you're familiar with the tooling already, but trying to figure out where some random method comes from in a rails code base when you're new to Ruby is somewhere between a nightmare and impossible without debugging and using the repl to tell you where the source is.

React has a JSX macro, and I love using it, so there's definitely room for them. There is a world of difference in developer experience when macros are used versus when not, however, and it is wrong to say that it is all the same.

cess11

10 hours ago

The counterpart in Laravel or Spring Boot or whatever would be annotations. As I understand it, that's how they're doing things on the .NET side too.

It's kind of the standard way to paper over the protocol grit of HTTP and make people able to quickly pump out fresh plumbing between outbound socket and database.

bmitc

16 hours ago

> Frameworks in langauges that don't use macros have this problem too that's what I was getting at with the DSL vs API thing.

You mean in the sense that the language's built-in syntax and available abstractions get abused so much that it approximates a DSL?

losvedir

an hour ago

Very cool! As a professional Elixir developer who loves types, I'm so happy to see them coming more and more to the language.

I have a question about how the type inference works. Dialyzer, which also attempts to do type inference, uses "success" typing which means it will not flag something if it could work. It tries to minimize false positives. In practice, this means it hardly ever catches anything for me (and even when it does warn about something, it's usually not real anyway!), so I don't find it that useful.

Does this approach use "success" typing as well? I found the `String.Chars` protocol example interesting, since I've had my fair share of crashes from bad string interpolations. But in the example, it's _clearly_ wrong, and will fail every time. That's not that useful to me because any time that code is exercised (e.g. in a simple test) it would be caught. What's more useful is if some particular code path results in trying to interpolate something wrong.

I know under the hood, the Elixir type system has something to do with unions of possible values, so it is tracking all the things that "could" be passed to a function. Will it warn only if all of them fail, or if any of them fail?

mcdeltat

17 hours ago

Elixir just keeps chugging along steadily releasing great features and improvements. It's an astoundingly well designed language and the creators have a solid approach to its development. It's a shame I don't get to use Elixir day to day.

recroad

16 hours ago

I quit my job and started a company just so I can use Elixir.

ipnon

43 minutes ago

Me too. No regrets.

pandemic_region

9 hours ago

How's that going for you?

ludicity

5 hours ago

Not the person you're responding to, but I also quit my job and started a company that uses Elixir for internal stuff.

It's overkill for some of our problems but it's working fine! We make mistakes, but they're mistakes we'd have made with most other languages.

I did have to buy Pragstudio licenses for anyone using Elixir on the team. I'd prefer a few books, but most Elixir/Phoenix books don't seem like they're keeping up with the rate of change.

recroad

2 hours ago

Pretty good. The company is jumpcomedy.com and I'm making less money but am much happier. Pair and mob programming have been hugely helpful in sharing learnings and patterns. We only have three developers - all remote. One very nice surprise has been how amazing LLMs are with Elixir, something about a clean functional language maybe, not sure.

Getting feedback directly from production has been helpful too to tell us when we didn't think something through. We don't use branches, everything is a commit to main and every push is a production deployment so all three of us are in the loop on what each other is doing.

Didn't really buy any books but testing and trying things out in LiveBook has been huge to learn the nuances of the language. The LiveDashboard has been great in monitoring things, especially the PostGres plugin for it. The Discord community has been very supportive as well, and the Elixir Forums as well.

asa400

a day ago

Just a data point on the deps compilation, on a small Phoenix app with mostly stock Phoenix deps:

  MIX_OS_DEPS_COMPILE_PARTITION_COUNT=1 mix deps.compile  32.30s user 7.23s system 320% cpu 12.336 total

  MIX_OS_DEPS_COMPILE_PARTITION_COUNT=5 mix deps.compile  0.37s user 0.49s system 12% cpu 6.970 total

  MIX_OS_DEPS_COMPILE_PARTITION_COUNT=10 mix deps.compile  0.38s user 0.50s system 12% cpu 7.236 total
Machine is a Mac M1 Max, `rm -rf _build` in between each run.

yuliyp

9 hours ago

You're measuring a cached compile in the subsequent runs. The deps.compile probably did some native compilation in the dep folder directly rather in _build.

dns_snek

8 hours ago

No their results are correct. It roughly halved the compilation time on a newly generated Phoenix project. I'm assuming the savings would be more extensive on projects with multiple native dependencies that have lengthy compilation.

    rm -rf _build/ deps/ && mix deps.get && time MIX_OS_DEPS_COMPILE_PARTITION_COUNT=1 mix deps.compile
    ________________________________________________________
    Executed in   37.75 secs    fish           external
       usr time  103.65 secs   32.00 micros  103.65 secs
       sys time   20.14 secs  999.00 micros   20.14 secs

    rm -rf _build/ deps/ && mix deps.get && time MIX_OS_DEPS_COMPILE_PARTITION_COUNT=5 mix deps.compile
    ________________________________________________________
    Executed in   16.71 secs    fish           external
       usr time    2.39 secs    0.05 millis    2.39 secs
       sys time    0.87 secs    1.01 millis    0.87 secs
    
    rm -rf _build/ deps/ && mix deps.get && time MIX_OS_DEPS_COMPILE_PARTITION_COUNT=10 mix deps.compile
    ________________________________________________________
    Executed in   17.19 secs    fish           external
       usr time    2.41 secs    1.09 millis    2.40 secs
       sys time    0.89 secs    0.04 millis    0.89 secs

dns_snek

8 hours ago

Similar result on one of my real projects that's heavier on the Elixir dependencies but that only has 1 additional native dependency (brotli):

    mise use elixir@1.19-otp-26 erlang@26
    
    rm -rf _build/ deps/ && mix deps.get && time MIX_OS_DEPS_COMPILE_PARTITION_COUNT=1 mix deps.compile
    ________________________________________________________
    Executed in   97.93 secs    fish           external
       usr time  149.37 secs    1.45 millis  149.37 secs
       sys time   28.94 secs    1.11 millis   28.94 secs
    
    rm -rf _build/ deps/ && mix deps.get && time MIX_OS_DEPS_COMPILE_PARTITION_COUNT=5 mix deps.compile
    ________________________________________________________
    Executed in   42.19 secs    fish           external
       usr time    2.48 secs    0.77 millis    2.48 secs
       sys time    0.91 secs    1.21 millis    0.91 secs

yuliyp

3 hours ago

Oh, interesting. I guess `time` is only reporting the usr/sys time of the main process rather than the child workers when using PARTITION_COUNT higher than 1?

asa400

17 hours ago

Anyone downvoting care to comment on why a benchmark of a feature in this release is downvote worthy?

Blackarea

17 hours ago

I really came to love gleam over the last few months. I appreciate elixir getting a type system and remember that this was the big NoGo for me when I explored it a while back. I'd like to give it another chance some time, but I'm worried that it's like typescript - looks typed on the outside but for many libs and packages the types are just a dynamic/any. Is my fear justified? Beam is amazing btw

barrell

9 hours ago

I would not entirely compare it to TypeScript. Due to pattern matching, legacy elixir will probably come out roughly 50% typed (extrapolating from my own usage, I'm not sure the accuracy of this number). Then since vanilla elixir is getting types (not a separate TypeElixir) I would venture anything actively maintained will get typed relatively quickly.

I personally do not like type systems, and still code in JS, not TS. Any JS artifacts I produce are untyped. Yet even my Elixir-code is nearly type ready.

So while TS is fighting an uphill battle, I think Elixir is working downhill.

innocentoldguy

15 hours ago

Gleam doesn't give you access to the full amazingness of OTP and BEAM. Elixir does.

abrookewood

15 hours ago

Why? Don't they all compile down to the same AST? How does Gleam prevent the use of OTP? Honest question - not overly familiar with Gleam

benzible

14 hours ago

They both ultimately produce BEAM bytecode, but Gleam compiles to Erlang source first (then through the Erlang compiler), while Elixir compiles to Erlang's abstract format. Gleam doesn't prevent OTP use - it has type-safe OTP bindings, but they're less mature/feature-complete than Elixir's, plus Elixir has better BEAM runtime integration (stack traces, profiling tools, etc).

Blackarea

12 hours ago

Profiling tools are something that I miss a bit in gleam - yes, but otherwise I had no problems with OTP bindings. The maturity of the ecosystem is a bit lower of course. But I actually enjoy finding libraries or repos with a few hundred lines. You gotta handroll some things but that's what I am here for. ffi to erlang and js for fullstack apps is as straight forward as it gets, but erlang syntax is indeed a bit crazy. For those interested in gleam but don't want to miss some necessities: https://github.com/gleam-lang/awesome-gleam (the gleam community is super nice btw).

For me some serious elixir adventure is high up in my todo list. But I remain suspicious if I can ever fully enjoy myself with a dynamic language - I think gleam and elixir do cater to different crowds. Gleam is pure minimalism (just pattern matching really), but elixir doesn't seem bloated either.

I am so happy that both languages exist and give alternatives in times of hundreds of node deps for any basic slob webapp.

rkangel

2 hours ago

When talking about Elixir (or Erlang) you are actually talking about two things that give you the value:

- A language (Elixir/Erlang) and runtime (BEAM) - The concurrency standard library (OTP)

The language and runtime give you the low level concurrency primitives - spawn a process, send a message etc. But to build actual applications, you rarely use those primitives directly - instead you use GenServers and supervisors. That gives yo a way to manage state, turns message passing into function calls, restarts things when they crash etc.

Gleam compiles to the same runtime, but doesn't provide OTP in the same way - static types aren't easy to make work with the freely message passing world of OTP. They are implementing a lot of the same concepts, but that's work that Gleam has to do.

(Elixir provides some thin API wrappers over the Erlang OTP APIs, and then also provides some additional capabilities like Tasks).

TeddyDD

9 hours ago

No REPL for example.

cess11

10 hours ago

Elixir has had types since forever, both primitive and things like structs and shape based destructuring. Then there's Dialyzer and libraries like TypedStruct.

It's never been a wat-language in the style of JavaScript.

cies

14 hours ago

Yeah Gleam. Or Kotlin on the JVM is also quite similar and has compile-to-JS if needed.

lawn

4 hours ago

Elixir can also compile to JS via Hologram!

Granted, it's alpha software and it's currently embedded in the Hologram framework, but still.

https://hologram.page/

Blackarea

11 hours ago

What? Gleam can compile to js. I don't see the similarity to kotlin being oo and all. Gleams compile time is lightyears ahead of anything on the jvm. I appreciate kotlins nullability constraints but to me it seems the jvm ecosystem is too grown to give kotlin the space it needs. Also heared that java's pattern matching and exhaustive checks have surpassed kotlins? In that case it's just easier to role with java, no?

jacquesm

7 hours ago

That's neat, Elixir is one of the most promising web development environments in my opinion, every time I come across it professionally it is a level above what we normally encounter, as are the teams that are using it. Elixir keeps setting standards for how you should approach this kind of continuous development.

ValtteriL

6 hours ago

>Elixir releases now include a Source SBoM in CycloneDX 1.6 or later and SPDX 2.3 or later formats.

Having the $lang_ecosystem address this sounds godsent. Unfortunately we don't use Elixir at $work.

BAPHOMETA88F

18 hours ago

Application specific type inference to compile ad hoc reporting using Elixir's Standard Library

dynamic() > boolean()

integer() > boolean()

cies

14 hours ago

I don't program in Elixir, but I'm a fan.

I was a fan of Ruby -- because of it's pragmatism and subjective beauty -- but then I got into type systems.

Elixir now also has a type system and, so does Ruby...

Though I know program in Kotlin, which syntax-wise is very much a "typed Ruby".

ch4s3

13 hours ago

Kotlin is almost the jruby we always wanted.

moomoo11

13 hours ago

I’m currently using soketi to broadcast events using a pusher sdk on my api services.

My app is a mix of real time and rest endpoints, and there’s no heavy computation and even if there was I could just call do that one off in Go or something.

Would Phoenix make sense for me? I have some cool collaborative features in the works.

29athrowaway

12 hours ago

Elixir was not a happy experience.

The language itself is maybe OK but the overall experience is not.

On a production build, stack traces look like Erlang code, which is the weird syntax that Elixir tried to improve upon.

Then you have macros, which make code unmaintainable at the 10k SLOC mark, and increasingly harder to maintain as projects get larger.

Running "mix xref graph" on most Elixir projects shows a spaghetti mess.

The toolchain has much room for improvement. Editing, debugging, profiling, unit testing, or pretty much any basic routine development task, involves a tool that's decades behind the state of the art. Even Borland tools from the 80s have a better toolchain.

Building a team around Elixir is hard. You have to train people on the job and they will probably not write idiomatic code that takes advantage of the language. Or deal with people that won't stop selling you how great the language is.

And the documentation for most of the projects you will use is full of noise, with few workable examples, grandiose claims of performance and fantastic treasures, and the articles are a great read if you want to waste your entire evening.

Support for massive concurrency is nice but you are realistically not going to need it. If you do need it then yes, Elixir can be a good tool for the job.

barrell

9 hours ago

I have the opposite experience. Even in production builds, I get Elixir stacktraces, and they're exceptionally helpful. Macros only save me time and LoC, I think maybe once I've struggled with a macro-caused issue (and it was immediately apparent that macros were the issue).

ExUnit has been hands down the most impressive testing library I've ever worked with, and the debugging, profiling, analytics, introspection, observability, etc of the BEAM is unbeatable.

Documentation of elixir, elixir deps, and elixir code is also far and above any language I've ever seen.

And the struggles I had supporting minimal concurrency in python were completely alleviated - so even if you don't need massive concurrency, elixir has a good chance of massively simplifying anything that needs minimal concurrency (which is probably most web related projects).

nobleach

3 hours ago

Even though I do not write Elixir on the job, I often cite their docs as a gold standard. They're succinct, easy to read, and most often show an example of how a method is used. What's better is that the language has documentation as a first-class citizen (it's even testable!) meaning your auto-generated docs will look as nice as the language's.

I complain about OCaml docs all the time. But Elixir? no way.

dns_snek

7 hours ago

> On a production build, stack traces look like Erlang code

Do you have an example? There are some cases that I can think of where the application dumps some foreign-looking data structures if the release fails to start, but that's very rare and usually the actual error is somewhere near the beginning like "eaddrinuse" here:

    [notice] Application my_app exited: MyApp.Application.start(:normal, []) returned an error: shutdown: failed to start child: MyAppWeb.Endpoint
        ** (EXIT) shutdown: failed to start child: {MyAppWeb.Endpoint, :http}
            ** (EXIT) shutdown: failed to start child: :listener
                ** (EXIT) :eaddrinuse
    Kernel pid terminated (application_controller) ("{application_start_failure,my_app,{{shutdown,{failed_to_start_child,'Elixir.MyAppWeb.Endpoint',{shutdown,{failed_to_start_child,{'Elixir.MyAppWeb.Endpoint',http},{shutdown,{failed_to_start_child,listener,eaddrinuse}}}}}},{'Elixir.MyApp.Application',start,[normal,[]]}}}")
Here's how runtime errors are normally reported (in `MIX_ENV=prod mix release` build):

    10:47:17.229 [error] GenServer {MyApp.Registry, "some-long-running-thing:4196f8ae-c971-439b-854e-5057e45076b9", %{}} terminating
    ** (RuntimeError) attempted to call GenServer #PID<0.2892.0> but no handle_call/3 clause was provided
        (my_app 1.1.0) /home/runner/work/elixir/elixir/lib/elixir/lib/gen_server.ex:895: MyApp.Monitoring.SomeServerMonitor.handle_call/3
        (stdlib 5.2.3.5) gen_server.erl:1131: :gen_server.try_handle_call/4
        (stdlib 5.2.3.5) gen_server.erl:1160: :gen_server.handle_msg/6
        (stdlib 5.2.3.5) proc_lib.erl:241: :proc_lib.init_p_do_apply/3
    Last message (from #PID<0.2891.0>): {:some_unknown_request, %MyApp.Monitoring.Stats{ts: ~U[2025-10-17 08:47:17.229542Z], some_data: 2}}
> Then you have macros, which make code unmaintainable at the 10k SLOC mark, and increasingly harder to maintain as projects get larger.

Absolutely, so don't write macro-heavy code. This is mentioned in the first paragraph of official Macro documentation and documented as an anti-pattern in the official documentation.

> The toolchain has much room for improvement.

I agree that editing experience (due to lacklustre language server support which is now being worked on officially), and debugging tools are lagging behind.

> And the documentation for most of the projects you will use is full of noise, with few workable examples, grandiose claims of performance and fantastic treasures, and the articles are a great read if you want to waste your entire evening.

I don't agree with this at all.

innocentoldguy

9 hours ago

I'm sorry you had a bad experience, but this doesn't reflect the experience of most Elixir programmers. I'll share my experience as a counterpoint.

> On a production build, stack traces look like Erlang code...

Elixir has the most readable stacktraces of any language I've used. Here's an example (which is color-coded in the terminal for even more clarity and, as you can see, doesn't contain any Erlang code):

  == Compilation error in file lib/app_web/live/authentication/settings.ex ==
  ** (MismatchedDelimiterError) mismatched delimiter found on lib/app_web/live/authentication/settings.ex:96:1:
    error: unexpected reserved word: end
     │
   4 │   on_mount {AppWeb.UserAuth, :require_sudo_mode
     │            └ unclosed delimiter
  ...
   96 │ end
    │ └ mismatched closing delimiter (expected "}")
    │
    └─ lib/app_web/live/authentication/settings.ex:96:1
It's easy to see that the issue is on line 4, and that it is a missing curly brace.

> Then you have macros, which make code unmaintainable...

Elixir gives you full access to the AST, making macros extremely easy to read and reason through. The point of the Lisp-style macros Elixir uses is to simplify your code. If your code becomes unmaintainable due to your use of macros, you're probably misusing them. I'd have to see a sample to make that determination, though.

> Running "mix xref graph" on most Elixir projects shows a spaghetti mess.

Spaghetti? It's a simple two-level tree that is in alphabetical order. Here's an example, and it is like this all the way down:

  Compiling 26 files (.ex)
  Generated stow app
  lib/app.ex
  lib/app/accounts.ex
  ├── lib/app/accounts/user.ex (export)
  ├── lib/app/accounts/user_notifier.ex
  ├── lib/app/accounts/user_token.ex (export)
  └── lib/app/repo.ex
  ...
To me, this output seems extremely accessible.

> The toolchain has much room for improvement...

Having developed many Windows apps using Borland's tools in the 80s and 90s, I disagree with this statement for these reasons:

• Mix is one of the best and most integrated build tools/task runners I've used. For example, you can create, migrate, and reset databases, execute tests, lint code, generate projects, compile, build assets, install packages, pull dependencies, etc.

• ExUnit is a great testing framework that handles all kinds of tests in an easy-to-read DSL similar to Ruby's RSpec.

• IEx is a fantastic REPL.

• Elixir's debugging tools are excellent. For example, IEx.pry lets you stop all processes and interact with your system in that frozen state in the REPL. You can watch variables, run functions, and even create new functions on the fly to interact with your data to see how it behaves in different scenarios.

> Building a team around Elixir is hard.

Why is it hard? I've worked exclusively on Elixir projects for both start-ups and large companies with hundreds of engineers for over ten years now, and never had a problem with hiring teams.

> And the documentation for most of the projects you will use is full of noise, with few workable examples, grandiose claims of performance, and fantastic treasures, and the articles are a great read if you want to waste your entire evening.

All technical documentation could be improved, but Elixir's is already quite good. See for yourself: https://hexdocs.pm/elixir/1.19.0/Kernel.html.

Furthermore, from within IEx, you can type:

  function |> h
for any function and see an explanation of what the function does and examples of how to use it.

> Support for massive concurrency is nice, but you are realistically not going to need it...

Elixir supports minute concurrency as well, and yes, I do need it. For example, in Ruby on Rails, which has a GIL, I'd have to use a gem like Sidekiq to push long-running processes into Redis so they can be processed in the background. In Elixir, I can just run them in a separate, concurrent process, which is simple.

Here's an example that takes a collection of users and a function and then runs each user through that function, each in a separate thread:

  defmodule ParallelProcessing do
    def map(collection_of_users, func) do
      collection_of_users
      |> Enum.map(&(Task.async(fn -> func.(&1) end)))
      |> Enum.map(&Task.await/1)
    end
  end
Here's the same Elixir code running in a single thread.

  defmodule SequentialProcessing do
    def map(collection_of_users, func) do
      collection_of_users
      |> Enum.map(func)
    end
  end
In the first example, I could have 1 user, 1,000 users, or 1,000,000 users, and this code would run as optimally as possible on all the cores in my CPU or across all the cores in all the CPUs in my multi-server BEAM cluster. There are no extra programs or libraries needed. In the second example, users are processed one at a time, similar to languages like Python, Ruby, JavaScript, PHP, and Perl.

Given the simplicity of writing parallel code in Elixir, why would I limit myself to one CPU core to perform a task when I can use all cores simultaneously?

> Or deal with people that won't stop selling you how great the language is.

The reason they won't stop selling you on Elixir is that Elixir is a fantastic language. I hope you take the time to revisit it in the future. It really is much better than most things out there.

behnamoh

17 hours ago

Elixir is still confusing, not the language, but the ecosystem, tooling, and philosophy. Is it dynamic or static? Is it compiled or not? If it's not, then why do we have Elixir scripts, which have different file extension? If it uses BEAM and you'd inevitably need to know Erlang when you hit the edge cases, then why not just learn Erlang? If it solves concurrency the "right way" due to supervision trees, why not use Python libraries that also implement the actor model, making Python code concurrent the "right way"? I just don't know which niche applications Elixir targets and excels at.

stanmancan

16 hours ago

I don’t understand why people drink orange soda. If you want something orange flavoured, eat an orange! If you want something bubbly, drink soda water!

.ex compiles to beam files to be run later

.exs compiles to memory

You don’t need to know Erlang to use Elixir; I’m a few years in now and I’ve never had to write any Erlang.

dnautics

12 hours ago

in the past i would have said learning to read erlang is a very nice to have for an intermediate elixirist but not really anymore, an LLM can translate between the two very easily.

jacquesm

7 hours ago

That's true. Even so I would still recommend that you learn to read Erlang because of its unique approach to various software development challenges. It will simply make you a better programmer. The same goes for LISP and possibly Forth because they have some very powerful concepts unique to those languages which you can mimic to some degree in other environments to get some of the benefits.

radiospiel

17 hours ago

> If it solves concurrency the "right way" due to supervision trees, why not use Python libraries that also implement the actor model, making Python code concurrent the "right way"?

I can't speak too much about Python – but immutable vars is a core prerequisite for many of the features OTP (the platform underpinning Elixir (and Erlang)).

thraxil

7 hours ago

Erlang/Elixir supervision trees also rely on process linking, which is implemented in BEAM and doesn't have a real equivalent in most other language runtimes (modulo some attempts at copying it like Akka, Proto.Actor, etc, but it's fairly uncommon).

jacquesm

7 hours ago

The difference in reliability between a BEAM based setup and one built on top of Python is so large I wouldn't know where to start to describe it. Erlang is built from the ground up to be reliable, Python has reliability bolted on as an afterthought and even the most advanced python environments (Anaconda, CPython) still suffer from some of the assumptions baked into the language.

distantprovince

14 hours ago

these are all surprisingly specific questions that can actually be answered!

1. It's dynamic 2. It's compiled 3. Elixir script is just a file with Elixir code that compiles and runs right away 4. I've been writing Elixir for 7 years and barely know any Erlang. I even submitted bugs to OTP team with reproductions in Elixir, they're chill. 5. Preemptive scheduler, immutable data

ch4s3

13 hours ago

Yeah I’ve been doing elixir professionally for maybe 10 years and have barely written any Erlang, maybe a dozen lines.

innocentoldguy

15 hours ago

Python has a GIL and is mutable, which makes concurrency impossible and error-prone, respectively. Elixir doesn't have a GIL and is immutable.

15155

14 hours ago

Python allows for memory sharing between threads - which is why a GIL is necessary.

A high-level language with a strict shared-nothing concurrency model doesn't need a GIL... but you naturally can't practically share very large objects between BEAM processes.

icedchai

13 hours ago

If you couldn’t share memory between threads, they’d be called something else. Many languages have threading without a GIL.

pjmlp

8 hours ago

Only on CPython, other implementations never had one, and it is finally gone on CPython 3.14.

innocentoldguy

11 hours ago

Hi, 15155. May I clarify a couple of things?

1. Regarding Python's GIL: The issue isn't memory sharing between threads. Java and Go allow you to do this, too, but they don't have GILs. The reason Python has a GIL is that it uses reference counting for memory management. If it didn't have a GIL, multiple threads could simultaneously manipulate reference counts, which would lead to memory corruption/leaks.

2. You can share massive "objects" between BEAM processes. For example, if you're running BEAM in a 64-bit environment, you can share maps, structs, and other data structures that are up to 2,305,843,009,213,693,951 bytes in size.

I hope this information helps. I also hope it is correct. I think it is, but I've been wrong before.

pjmlp

8 hours ago

1. not really, you can do this with atomic operations, which is now what CPython does, since GIL removal got final.

pjmlp

8 hours ago

Not really.

CPython used to have a GIL, it is no longer the case since the latest version, 3.14.

Other Pythons, jPython, GrallPy, PyPy, never had a GIL.

Languages and implementations aren't the same.

int_19h

3 hours ago

It still has a GIL unless you're using the free-threaded build, which most people do not.

pjmlp

3 hours ago

Well most people aren't using version 3.14 yet, and it depends on the platform installer.

However it is still a CPython issue, it is already available for those that install the right version, and no longer considered experiemental.