How I Fell in Love with Erlang
Posted2 months agoActiveabout 1 month ago
boragonul.comTechstoryHigh profile
calmpositive
Debate
60/100
ErlangProgramming LanguagesFunctional Programming
Key topics
Erlang
Programming Languages
Functional Programming
The author shares their personal journey of falling in love with Erlang, a functional programming language, and sparks a discussion about its strengths, weaknesses, and accessibility.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
17h
Peak period
126
Days 9-10
Avg / period
22.9
Comment distribution160 data points
Loading chart...
Based on 160 loaded comments
Key moments
- 01Story posted
Nov 1, 2025 at 5:00 PM EDT
2 months ago
Step 01 - 02First comment
Nov 2, 2025 at 9:06 AM EST
17h after posting
Step 02 - 03Peak activity
126 comments in Days 9-10
Hottest window of the conversation
Step 03 - 04Latest activity
Nov 26, 2025 at 2:49 AM EST
about 1 month ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
ID: 45785300Type: storyLast synced: 11/20/2025, 8:23:06 PM
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
Erlang gets a lot of stuff right for scalable web based stuff, and even though there are many of its influences that have by now made it into other languages and eco systems it is still amazing to me that such a well thought out system is run with such incredible modesty. You'll never see the people behind Erlang be confrontational or evangelists, they just do what they're good at and it is up to you whether you adopt it or not. And that is an interesting thing: the people that are good at this are writing code, not evangelizing. If I had to reboot my career I'd pick this eco system over anything else, it has incredible staying power, handles backwards compatibility issues with grace and has a community that you can be proud of joining. Modest, competent, and with a complete lack of drama.
There's been little drama, the language is relatively stable, the community has always been there when you need them but aren't too pushy and flashy. It all feels mature and – in the best possible way – boring, and that is awesome.
It's all so boring it's wonderful.
Of course, there are some changes that you need confidence in before you push, but for lots of things, a bit crashy as an intermediate step is acceptable.
As for understanding the OTP stuff, I think you have to be willing to look at their code. Most of it fits into the 'as simple as possible' mold, although there's some places where the use case is complex and it shows in the code, or performance needs trumped simplicity.
There's also a lot of implicitness for interaction between processes. That takes a bit of getting used to, but I try to just mentally model each process in isolation: what does it do when it receives a message, does that make sense, does it need to change; and not worry about the sender at that time. Typically, when every process is individually correct, the whole system is correct; of course, if that always worked, distributed systems would be very boring and they're not.
Because it's possible to do hot code reloading, and since you can attach a REPL session into a running BEAM process, running 24/7 production Erlang systems - rather counterintuitively - can encourage somewhat questionable practices. It's too easy to hot-patch a live system during firefighting and then forget to retrofit the fix to the source repo. I _know_ that one of the outages in the previous job was caused by missing retrofit patch, post deployment.
The running joke is that there have been some Ericsson switches that could not be power cycled because their only correct state was the one running the network, after dozens of live hot patches over time had accumulated that had not been correctly committed to the repository.
I had thought there was a way to get the currently loaded object code for a module, but code:get_object_code/1 looks like it pulls from the filesystem. I would think in the situation where you a) don't know what's running, and b) have the OTP team on staff, you could most likely write a new module to at least dump the object code (or something similar), and then spend some time turning that back into source code. But it makes a nice story.
[1] https://www.erlang.org/doc/apps/kernel/code.html#get_object_...
But, that's not enough to tell you that the code on disk doesn't match what it's supposed to be. You'd need to have some infrastructure that keeps track of that too. But if you package your code, your package system probably has a check, which you can probably also run at 4 am.
Two things that definitely helped me understand were reading the somewhat-dated-but-still-useful material on the topic in Learn You Some Erlang, as well as reading through the "OTP Design Principles" section of the Erlang System Documentation.
It makes it very powerful but very disorienting and experience gained with one part of it often does not really prepare you for other parts. Usually each specific tool was created by someone who used it immediately, so it's all reliable in its way. But there is a lot of redundancy and odd gaps.
Elixir's almost extreme attention to naming, organization, and consistent convention is almost as far as you can get from this approach too. It's fun to have them in the same ecosystem and see that there are actually pros and cons to each approach.
I generally agree with you that learning Erlang stuff can be daunting.
I will say that many things worth doing are not easy! Erlang and the whole OTP way of thinking is tough to learn in part because it is genuinely different enough from everything else that is out there that one's odds of being familiar with its conceptual underpinnings are low.
If you have trouble learning Erlang (and OTP specifically) it's not because you're dumb, it's because Erlang is different.
Learning Erlang is not like learning any other dynamic language you've learned. Learning Erlang is closer to learning a bespoke operating system designed to build reliable low-latency long-running systems. It's a larger conceptual lift than going from one dynamic OOP language to another dynamic OOP language.
The big open source projects where pretty much all like that in the past, in the 80's/90's/early 2000's - in that respect they feel like a pleasant anachronism before everything needed to be promoted/self-promotional influencer like, the users did the evangelism but the creators where usually much more chill.
Obviously the vast majority of open source projects are still like that but there is definitely a lot more in your face promotion of things that feels different somehow almost aggressive/corporate style even when there is no paid product.
Not knocking the ones who do it, if it's open source they can sing it from a mountain top for all I care, the license it's under matters more.
So doing some level of promotion becomes necessary if you want users even when you have the better product - the superior product speaks for itself doesn't often apply any more.
It actually never did, for almost any product.
And programming languages are in the lower end of quality actually impacting decisions. People are incredibly resistant to changes there, and just can't evaluate competing options at the same time.
Take Swift for example. A giant gatekeeper of a corp decided to make it the only (reasonable) way to build apps and so it exists, powered by countless indie developers constantly creating content around it. Would Swift be a thing without everyone being forced to use it? I don’t know, but I don’t think so.
So in some ways we’ve traded unique and effective solutions to “popular and mainstream” things that scream the loudest. You wouldn’t get fired for choosing Swift. Or Azure.
Last time I worked with it it felt very sluggish and buggy though, in theory building UI elements with SwiftUI is great, in practice it was slow and needed to restart very often, and that was with simple components.
If you’re creating that closed of an ecosystem, at least learn from history and create something like smalltalk.
For those who collaborate with open source for political/ideological reasons (which does not need be the case), it makes sense to join the battle for attention.
As long as the product isn’t compromised in the way, I think it’s very good to see open source influencers.
It's hard to describe precisely, but a lot of free software projects do a good job of putting themselves out there in an unfussy way. There really is something refreshing and cozy about that.
I must have been living in a different world then. I mean maybe in the 80's and 90's but I feel like people acting weirdly obsessive about a piece of tech and going about evangelizing it every where, usually in a detached from reality kind of way, goes back to at least newsgroup, when suddenly you could have an audience outside of physical event (with their limitation and all). I mean there was the text editor flame wars, and I am sure you can find post like "why are you not using language/database/tool X instead of Y???!!" in the most ancient of mailing list and forums.
I've learned Elixir in 2016 after a lull in my interest in programming languages, and 9 years later it's still my favourite environment by a country mile. It's not the language per se, but the BEAM, the actor model, the immutability — just makes sense, and doing things the C/Rust/Javascript/Python way is like building bridges out of cardboard.
For example, I've stepped into the world of game dev and Godot, which is fantastic and uses a regular object-oriented model. After trying to build a non-trivial desktop app with it, my thoughts are consumed by the fact that mutable state and object orientation is the silliest idea, and I'm speaking as someone that really got into Smalltalk and message-passing objects.
I don't even need actors and OTP, I just want some immutable data structures and functions operating on them. Erlang/Elixir are fantastic to build servers, but there is a sore lack of something closer to the metal within 80% the speed of a native language. I would build an entire operating system out of it. Why has no one put microkernels and Erlang into a blender? I know there's QNX, but it's still UNIX, not Erlang.
Thanks for the reminder.
That's a very good question. There are some even lesser known dialects out there that do this but you are going to find it hard to get to the same level of feature completeness that Erlang offers out of the box.
QNX and Erlang embody quite a few of the same principles, but QNX really tried hard to do this at the OS process level in a way that destroyed a lot of the advantages that doing the same under Erlang would have. I think the main obstacle is the fact that the CPU does not support reductions natively. Maybe you could take it a step further and design an FPGA CPU that implements the core features of Erlang at the hardware level?
That would be an absolutely awesome project. Usually when you can think of it someone has already done it so a bit of googling would be a good way to start with that.
A bit of googling indicates that actually you can use performance monitoring instur to generate an interrupt every n instructions. https://community.intel.com/t5/Software-Tuning-Performance/H...
Which is part of the solution. Presumably the remainder of the solution is then deciding what to schedule next in a way that matches erlang.
Disclaimer: this is based off some googling that makes it seem like hardware support the desired feature exists, not any actual working code.
But: great find, I wasn't aware of this at all and it is definitely an intriguing possibility.
I've done a bit of googling and research, nothing viable has surfaces, and I still haven't found the time to create a prototype myself, just some doodling around here and there. I do agree that it's an awesome idea, and it's been stewing in my head for a couple years now!
There are a lot of moving parts (scheduler design, probably needs its own high level language and bytecode, mapping capabilities onto actor semantics, etc.) that are outside what current OS-research seems to be focused on.
EDIT: I've just seen the mention of your QNX-like OS in your profile. Lovely! Any reason you haven't posted it on Github/Codeberg?
Features include: x86-32 only, bios boot only, SMP capable, drivers in Erlang (there's nifs for managing memory with devices or i/o; and the kernel manages the interrupt controller(s) and has console output before userspace takes over), a kind of working IPv4 stack, dist!
It doesn't run on all my machines, but it does on some (it does some VGA things that I guess aren't well supported and also non uefi is iffy these days too. I typically run it in qemu and v86, but I think it will work in VMWare as well.
https://crazierl.org/ https://github.com/russor/crazierl/
There's a hosted v86 demo as well (works better on a desktop than a phone):
https://crazierl.org/demo.html
It's also in the weird OS section at https://copy.sh/v86/
If it doesn't take much time, it's worth trying to get it to run in v86; it's so much easier to send people a link to a web page to poke at your OS than to send them an image and tell them to run qemu against it.
Edit: I misinterpretted --- you'll carve your OS up, not mine, that makes more sense!
Old comment: Feel free to carve away, just be aware that just because it's committed doesn't mean it works... I wouldn't take my memory management code, for example. There's some fiddly issues I haven't tracked down because it doesn't break consistently.
[1] https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...
Yes, I don't want Microsoft to be able to pretend claim I gave it to them for some particular purpose which I didn't. They'll have to come and take it.
Feel free to use that code and do anything you want with it, and if there are no more seeds for the code let me know and I'll serve up the torrent.
Clojure has that for you. plus its jvm which means for your desktop use cases it works. hell with graalvm can be ported to native.
And then there’s the whole evaluation instead of instructions. With FP, you’re always thinking recursively. With imperative, you can coast on a line by line understanding.
I wish most language / framework designers knew that part.
again - another point why not hiring junior developers cz A.I or outsourcing those will cause catastrophic effects in the future.
> JOSE: Yeah, so what happened is that it was the old concurrency story in which the Clojure audience is going to be really, really familiar. I’ve learned a lot also from Clojure because, at the time I was thinking about Elixir, Clojure was already around. I like to say it’s one of the top three influences in Elixir, but anyway it tells this whole story about concurrency, right?
https://www.cognitect.com/cognicast/120
Well, we have FP at home.
It just got ingrained into pretty much every mainstream language, and most [1] of the wins can be had even when it's applied to certain parts of the code base only. Like, Java has immutable data classes (records), ADTs with pattern matching, etc.
As much as I like Clojure, I just don't think Lisps can ever become too mainstream, their readability simply repels too many people (and even though I am okay with reading it, I do think it's less readable than an equivalent Java-style code would be).
[1]: I would even argue that a properly mixed pure and (locally) side-effecting code is the happy ground, as they strengthen each other
I can't find any.
1) I suspect game engines that Carmack designed towards the end of his career are built in a largely-functional style. He's on record [0] as thinking that writing code in a functional style is generally a good thing to do.
2) Running on the BEAM doesn't mean that you give up mutability. In my experience with Erlang, functions that return updated state are very, very, very common. Similarly, functions that mutate state external to the function [1] are also common... it's rare that a program that has no visible effect on the outside world is useful.
It's generally quite a lot easier to understand what your program does when most or nearly all of its functions take input and return output without mutating external state as a side effect.
[0] There's some Twitter thread of his that I CBA to find saying -in effect- "Writing in a functional style makes your code much easier to understand. Obviously, if you're writing a video game, you have to pass around pointers for efficiency reasons, but that doesn't preclude writing most of your code in a functional style."
[1] Such as in an ETS table, a external database, with a network call to a remote system, or even writing data to disk.
And (as a fun fact) did you know that Crash Bandicoot, Jax and Daxter, and many other Naughty Dog games were written in Lisp? I expect that more video games sold at retail back in the day were -whether entirely or just in part- written with weird languages and runtimes than we would expect.
But, to answer your question: I don't pay much attention to who's doing what in video games, so I don't know for sure. Folks often talk about using Erlang for video game servers, but not so often about video game frontends.
I do know that Erlang's said to be a pretty bad fit for -say- 3D video games that require high raw performance. Sections 1.3 and 1.4 of the Erlang FAQ [0][1] provide a decent idea of the sort of things for which it is and is not a good fit. Particularly relevant would be these excerpts:
But you should really read those sections of the FAQ for yourself (while remembering that they were written like twenty years ago).Also relevant is this excerpt from Learn You Some Erlang's introduction chapter in the "Don't drink too much Kool-Aid" section, written in ~2010: [2]
Having said that, I play a lot of video games. Based on what I've seen, I expect that most indie video games these days could easily eat the constant factor introduced by using Erlang for coordination.Having said that, with the existence of Unity and Unreal Engine, along with the documentation and assets built for both, why would you?
[0] <https://www.erlang.org/faq/introduction#idm27>
[1] Those FAQ sections have remained largely unchanged for something like twenty years. Computers have gotten much faster (and the Erlang VM and compiler have gotten much better) over that time, making the "constant factors" introduced by Erlang smaller than they once were.
[2] <https://learnyousomeerlang.com/introduction#kool-aid>
Wich means:
GAME ORIENTED OBJECT LANGUAJE....
The example you gave about how functional laguages can make comercial videogames... has objects at the core of its design.
No. As I said:
> I expect that more video games sold at retail back in the day were -whether entirely or just in part- written with weird languages and runtimes than we would expect.
And again, you said CB was writen in LISP, wrong.
The multitasking, multi-user system known as UNIX was built on and for the PDP-11. The PDP-11 was only 7x larger than the NES.
FORTRAN 77 (from 1977) is roughly contemporary to the PDP-11, as are many other languages still in wide use today.
Back in the day, compilers, interpreters, and virtual machines were all things that were regularly used... and state-of-the-art computers were smaller than you'd expect.
Most things are said to be impossible until someone tries to do it anyway.
If 15 years ago you said games can't run on managed laguages you knew nothing about videogame development.
They use it, but the best tend to minimize mutability. And as for object orientation- merely 'having classes' is not the same thing.
Peer Stritzinger (https://stritzinger.com/) the guy behind the GRiSP project (https://www.grisp.org/) has integrated Erlang + RTEMS (https://www.rtems.org/) for the embedded GRiSP Nano HW (https://www.grisp.org/hardware#grisp-nano-details-section and https://www.grisp.org/blog/posts/2025-06-11-grisp-nano-codeb...)
His HN account with some technical comments - https://news.ycombinator.com/threads?id=peerst
I just remembered Kry10 OS which runs Erlang/Elixir on seL4 microkernel - https://www.kry10.com/ Checkout the technical overview paper at https://www.kry10.com/get-started
Some excellent videos linked to from https://elixirforum.com/t/kry10-a-robust-and-secure-os-for-t...
Kry10 Secure Platform - https://www.youtube.com/watch?v=YG5BaoB24eA
The Kry10 Operating System: Security and the BEAM - https://www.youtube.com/watch?v=0ncI0I5uxJ4
seL4 and BEAM, a match made in Erlang - https://www.youtube.com/watch?v=owmvgUwBlJ0
As someone who built an entire startup in elixir, its a good time to bring up that elixir has an excellent interop with rust via rustlr.
https://github.com/cloudozer/ling
Erlang on Xen.
But also one of today's unlucky 10K as it hasn't been updated for 10 years.
I have nothing but admiration for Erlang, and it is, without a doubt, one of the most inspired languages I've encountered in my career. But when I was at university in the late-ish nineties, they taught us Haskell as "the language of the future." So I guess some languages are forever languages of the future, but they still inspire ideas that shape the actual future. For example, Erlang monitors were one inspiration for our design of Java's structured concurrency construct [1].
If you're interested in another "language of the future" that bears some superficial resemblance to Erlang, I'd invite you to take a look at Esterel (https://en.wikipedia.org/wiki/Esterel), another language we were taught at university.
[1]: https://docs.oracle.com/en/java/javase/25/docs/api/java.base...
So there are definitely unpleasant people around, just like in many ecosystems. Maybe less so, but I don't know about that. The question is only whether one lets that keep one from doing whatever one came to do.
Every group of > 10 people will have at least one jerk in it.
Can I interest you in my Rust project hosted in Jujutsu VCS?
It's colourful language, but it's just a stand-in for other properties you might care about. For instance, in head(sort(list)), will the whole list be sorted, or will the smallest element be returned? In atomically(doThis(); doThat()), will doThis be 100% reverted if doThat fails? If you stick to pure functions (and in the second example, STM) then you can unfire the missile!
AFAIK, Erlang just fires the missile "over there", not "over here". The author jumped from:
to I'm not bashing the BEAM, or the ease with which one can send messages (as an Actor language), but I am complaining about the lack of tooling to write non-missile-firing functions on a single node (as a Functional language).The author did not say this at all, they barely even touched on capabilities of erlang/OTP. Their focus was on the functional syntax of Erlang.
> For instance, in head(sort(list)), will the whole list be sorted, or will the smallest element be returned?
Your point isn’t clear. The functions have a clear nested call sequence, take a list, sort it, get the head.
Also how is it any different than Haskells `head (sort list)`?
They didn't write any Erlang until the ping/pong example, which doesn't have any functions. What does pong() equal? Is it equal to itself even? What's its domain and range? If I wrote a unit test for it, what test inputs would I give it and what outputs would I assert?
If the author actually talked about the capabilities of Erlang, they would -at minimum- answer the questions that you'd raised, that the return value of both 'ping/1' and 'pong/0' are irrelevant because they are ignored, and that the range of 'ping/1' is not only infinite, it can accept any type for which it's legal to '+ 1'. [0] They would have also mentioned why they call 'ping' and 'pong' at the end of the respective function, the reason for which is kinda strange if you're coming from a more-normal language.
One can add annotations to functions that indicate what their input and output types are, and if you do a little bit of work, you can also indicate valid ranges/values for those types. These annotations are not checked at runtime, but can be checked by tools such as dialyzer. But, because this blog post barely even touched on Erlang/OTP's capabilities, none of that was mentioned.
[0] I think the valid types are only integers and floats, but definitely don't bet your career on that information.
The author pivoted from rejecting "change state of X" to advertising "change state of mailbox".
> the return value of both 'ping/1' and 'pong/0' are irrelevant because they are ignored If they were functional, they'd be optimised away by the compiler (for being irrelevant). They only exist for their side effects. Ping and pong are procedures. This is 100% imperative.
What distinction are you drawing between functional and imperative programming? This is not a trick question. Simon Peyton Jones has described Haskell as "the world's finest imperative programming language", and one way of interpreting my Haskell effect system Bluefin[1] is "purely functional imperative programming". I don't think functional and imperative are are mutually exclusive, in fact, I don't think they're even at odds. I think they're both at their best when combined.
[1] https://hackage.haskell.org/package/bluefin
Yes, these are really obviously two functions that exist to mutate state by printing to a console somewhere then communicating with another process.
> If they were functional, they'd be optimised away by the compiler (for being irrelevant).
I'd expect -say- Haskell to not optimize away functions that exist just to print to a console.
So, is your complaint that Erlang is (like Prolog) not a purely functional language, and that the author has asserted that Erlang is a functional language (in the same way that Prolog is) [0] but provided example code that produces side effects?
[0] Be very careful when reading here. I'm fairly aware of just how much Prolog is a functional language.
It is declaring a relationship, between the previous value and the current. One way or another, youre defining transformations.
I mean even in the sum example, you see the statement "N is n-1" which is the exact same thing as x = x+1 with = swapped for "is"
That wasn't actually what the example said. It said N1 = N - 1, and continued using the N1 value somewhere else. In that example, no actual mutation occured.
Then I have found the code that is a heavy user of closures is harder to understand even if it is single-threaded and closures can pass arbitrary state even if the state is immutable.
What I have found useful is persistent data structures. Those really simplify modelling. But then those can be used in imperative languages as well.
I'm saying this with complete sincerity: WHAT IS IT THAT YOU PEOPLE SEE!? What is the fun? What are you addicted to? Typing and seeing the output? Solving a problem?
I feel like I am missing out on some amazing life altering experience when I see people state that. The same thing I have with the article - what does it mean to love a programming language?
And then, one day you discover the chainsaw, and the fact that you should cross cut.
Suddenly cutting wood no longer feels terrible, it's smooth, stuff works the way you expect. Your chainsaw turns out to have features for all kinds of wood and for many different kinds of joinery that you were hand carving before. It turns out that your chainsaw can be used to control an army of chainsaws without having to do anything at all. You just declare it to be so and it works.
Instead of focusing on your tools all day long you find yourself focusing on the thing that you are actually trying to solve. Where you needed 50 people before, now you need 5.
https://xkcd.com/224/
Also this:
https://www.infoq.com/presentations/Simple-Made-Easy/
For me, there's a dopamine hit in taking a complex problem, and breaking it into simple interacting parts that solve the problem in an elegant way. Overly complex programming languages add lots of incidental complexity that slow down this process. A clear, simple, consistent semantics accelerate this process.
If that doesn't inherently excite you, the life altering experience probably isn't going to happen for you.
> “My God, it’s full of ‘cars’”
Classic
I've fought null pointer exceptions longer than I want to in C# servers and Python code. Usually because we're introspecting into a message that a system sent us, we expect it to have a field on the message, we failed to check for the existance of that field / the field to not be null, and bam null-pointer exception.
Sure, add another guard clause... Every time you run into it. Every time the server crashes and it's a serious problem and someone forgot to set a restart policy on the container... Sometimes you don't know exactly what cascade of messages caused the whole thing to unwind. So tedious. So repetitive.
With GenServers, you've carefully defined that those parts of your system have active, in-memory state, and everything else must be a pure function. Messages come in a FIFO queue to your GenServer.
With pattern-matching, you define exactly what shape of message / fields on the message, and if it doesn't match that, you can either log it, or crash that GenServer.
Which normally would be catastrophic, but since you tied your GenServer to a SupervisionTree with a restart policy (which is trivial to write), if anything happens that you didn't expect, you can Let It Fail. The GenServer crashes and is immediately restarted, with a clean slate, with the correct peer-services in the correct state. Sure, the message that crashed your server was "lost" (there are options to push it to a dead-letter-queue on exit) and you have a bad-state and bad-message to debug in the crash-dump, but your server is still going. It hasn't stopped, it just dropped unexpected messages and has a deterministic recovery pattern to clear all bad-state and start again.
So instead of your message handling code starting with a bunch of defensive sanity checks, with the real meat of the function in the last 5 lines.... you just say "My GenServer is here in this SupervisorTree, I expect an inbound message to look roughly like this, and I will do this with it, done". Suddenly the server will handle the messages it knows how to handle, and everything it cannot handle it will drop on the floor.
Think of this! The server just stays up and keeps going! Anything it cannot handle is not a fatal exception, but a log message. And you didn't have to bend the code into an unnatural shape where you trapped and logged all exceptions and bubbled them up in specific error checks... it's just designed to do it that way out of the box.
In that sense, Erlang isn’t really different from any other functional programming except that it also ships with a lot of actor / message passing concurrency dogma which solves a different and equally delightful set of problems (low-bug concurrency) at the same time.
So it’s a double hit of two great ideas in one paradigm.
*Oops, no pun intended.
These are just bombastic claims and empty verbiage which has become "language du jour" in the Interwebs. This came into vogue during the software boom of the 90s when companies started asking for "rah-rah passion" and everybody started making such inane statements to get through interviews.
As for the submitted article, it is just pedestrian (there is nothing in it really) with some pretty posturing/language to sell it.
But some of comments in this thread are informative.
Expressiveness: I could express a solution to a problem with very few lines of code and without the clutter of housekeeping operations like memory management.
Recursion: Erlang properly introduced me to recursion whereas I hadn't encountered it before. This is again related to expressiveness. There is something strangely beautiful about seeing a problem solved using recursion elegantly.
Message passing: when I was trying to figure out how Microsoft's C++ socket classes were implemented and I dug into the code, it turned out there was a hidden window for every socket created and messages were being passed to/from it, but message passing wasn't available anywhere in Visual C++ as a first class construct (at least as far as I remember it). I was overjoyed when I discovered that message passing was a first class citizen in Erlang and how much easier it was to implement concurrent programs than using native threads in C++.
Compared to OO programming in C++ where I was never sure whether I was using inheritance correctly, whether I needed inheritance at all, memory management, difficulty with threads, writing code in Erlang was a breeze.
And the whole support for distributed programming, hot code loading, list comprehensions! I fell in love again with Erlang when I discovered list comprehensions. Man, I could go on.
And for me, what it comes down to, is, doing powerful things in Erlang/Elixir is trivial where it would be really convoluted in other languages. When applied to the right problem, it genuinely makes a whole class of problems evaporate, and that's addicting.
I'm a hobbyist high-availability dork, so the idea that I could update my program without interrupting any user of the program was very, very attractive to me. I'm also quite sick in the head and have a life-long torrid love affair with Bash's switch statements, so Erlang's "pattern match to make most decisions" syntax was a huge attracter.
Having said that, I didn't really get Erlang until I had a project for which it and the OTP were a good fit. I needed to build a server for an unfamiliar-to-me protocol, so I expected to regularly have protocol handlers fail due to incorrect implementation. This server would be long-running, so VM startup time was not a problem. This server would perform next-to-no number crunching. This server could be very effectively modeled as a swarm of message-passing processes. The core functionality of the server was best modeled as an FSM.
Erlang's "share nothing, use message passing" design, along with OTP's supervisor and friends kept my server up and running while my protocol handlers exploded because of incorrect or absent handler code, or too-pessimistic assertions about the valid bounds of input data. Hot code reloading let me fix broken code paths (or experiment with non-broken ones) without affecting users of other code paths in the server. [0] The built-in FSM library made it trivial to express the parts of my program that were an FSM as an FSM. Not only did Erlang's syntax satisfy my sick fascination with Bash's switch statements, it permitted me to write nearly all of my server as simple, sequential code and let Erlang/OTP handle the nasty problems of concurrency for me.
Oh yeah, and the extremely high quality of Erlang's documentation was really helpful. In the reference manual for the standard library, the documentation for every single function provided in Erlang/OTP told you the valid types and acceptable ranges/values for both function arguments and return values. They told you which functions would throw, what would be thrown, and under what conditions. They also described what the function did, and -when needed- why it did it. I could be confident that if I programmed according to the docs, then my program would behave as the docs said it would... unlike where documentation for most Web Development stuff leaves you. [1] There's also official documentation about the design of Erlang/OTP and the reasons behind that design. Those docs (along with Learn You Some Erlang) definitely helped me understand Erlang and OTP.
Like I said... if your project isn't a good fit for what Erlang provides, I think you're not going to get what makes Erlang/OTP special. But if it is, there's a very good chance that you will.
[0] Supervisors + hot code reloading made it emotionally really easy to build my server incrementally. Knowing that I didn't have to get the entire protocol right to have a server that would never die was calming. As was knowing that implementation failures (or deliberately unhandled parts of the protocol) would provide me with usually-good diagnostic information, rather than a core dump (or nothing at all(!)).
[1] This was prior to the big redesign of Erlang's docs to ape the style used by HexDocs. Whoever did that redesign very, very clearly did not understand what made Erlang's documentation so good. Switching from EBNF-ish to raw Erlang spec format not only takes up far more vertical space, but adds yet another new thing someone new to Erlang needs to learn. But far, far, far worse is that some of the documentation about the valid ranges of input to functions has been lost.
A good example is mutable state: for a bunch of people, functional languages that enforce immutability has this calming effect, since you know your data isn't mutating where you can't see it. You've been burned by C++ code that's passing references as arguments, and you don't know if the list you received as an argument will be the same list after you've passed it as an argument to a different function. You don't know if you can futz with that list and not make a problem for someone somewhere else.
But for most people, they much prefer how "intuitive" it is to have mutable state, where they just change the thing in front of them to be what they need. This is especially true in the context of for loops vs. recursion: "why can't I just use a for loop and increment a counter!" A lot of Golang folks love that it explicitly rejects functional mapping primitives and "all you need is a for loop."
It's a very personal decision, and while IMO it doesn't really matter for the ultimate business success (usually companies fail because of something that's not tech-related in the least), it does shape _how_ it feels to work on a tech stack, and I'd argue, what kinds of technical problems you run into.
As for it being fun and addictive, I'm retired. I can write code in any language I want, or don't write at all, and I choose to write in Elixir.
Also, some languages just really stink— slow compilation times, obscure errors, loads of footguns and require much more care and focus on things which are orthogonal to the problem you’re trying to solve.
Programming languages stress reusable code, where you get reusable processes (eg interface to the SMSC)in Erlang, which you would need messaging servers in other languages.
Some of the comments here are far more informative.
That's really interesting... My wife, who has no real mathematical background had the EXACT same reaction when I was trying to teach her some simple programming. I tried to explain that equals in that context was more of a storage operator than a statement that said line is true. She found it very frustrating and we gave up on the endeavor shortly thereafter.
I've personally always had a soft spot for languages like TI-BASIC that use a storage operator rather than overloading = so for example:
X + 1 -> X
I wonder if I should try a functional language with her.
X := X + 1 is perhaps less confusing, even if meaning the same thing.
I think an easy way to look at it, for someone coming from a math background, is to think of programming lines as instructions, instead of statements. x=x+1 can be then read as "let x be x+1", and that's it.
https://en.wikipedia.org/wiki/Assignment_(computer_science)
I don't think this is something that keeps someone from programming. If it does, then the other 100000 hoops won't be better, every trade has its "why the fuck is this the way it is" moments.
If you'd still try programming with her, I think you could start with a visual programming thing. Maze from Blockly Games is a simple online logic game that can be overcome with visual programming. No need to register or anything. The levels get progressively harder, as it teaches to apply programming concepts, and makes the player combine them. As a programmer, I found the game really fun, and I think it's suitable for beginners as well, seeing how for example LEGO used a very similar system for its programmable sets.
https://blockly.games/maze?lang=en
Not everybody is wired the same way. That exact thing happened to me, it was some kind of mental block. And when that fell away I found the rest of programming to be fairly easy.
I guess I got through that when I was 7 on a pass-me-down ZX81 from a relative who just had upgraded to a Spectrum (I assume).
I mentally called bull when reading boragonul's story because:
a) BASIC has a REPL
b) Kids back then had near infinite patience/time.
c) These computers came with pretty good manuals explaining BASIC
There was very little external stimuli. As an example: TV broadcasters in Europe were literally mostly shut off during the day to save power. Radio was on all day, but it was 99% boring adult stuff.
Those first years were a real slog. I wanted to understand this stuff so much but it just did not click. I actually remember when it did, it was like a lightbulb going on and I went from 'this sucks, I can't hack it' to 'this is my future' overnight.
The I read a book by Niklaus Wirth and it opened my eyes to structured programming and various data structures (beyond variables and arrays). It's funny because even though I've read a mountain of books on computer programming by now that is still the one that gave me the most practical day-to-day knowledge which comes in handy every day.
These days it's almost painful to come across great books that would have been so awesome to have in like 1984.
Between that and a book on parsing I managed to cobble together an assembler and an editor together with a friend.
Oh, and in the list of machines I forgot the ST, the first machine that I had with a megabyte of RAM.
It's ridiculous, usually fake and hide the biases of those who nurtured them to believe whatever they believe, even if it's by pure imitation.
So you're absolutely right to call BS.
X = X + 1
and equality as in
IF X = 3 THEN 'Do something
and it's as natural as 'to dust' meaning both removing the dust and adding dust, depending on context, and 'to sanction' meaning both to approve and to punish. And the Prolog way seems all good too. There are more than one way to think about things.
Also, various ways of assignment:
Pascal style:
Scheme: Forth:I gave a talk at Midwest.io (sigh, such a great conference, shame it faltered) building Erlang from the ground up, starting with the = sign and the implications (side effects?) of it being a runtime assertion of truth in addition to a binding.
https://youtu.be/E18shi1qIHU
For example
is really88 more comments available on Hacker News