Took me a long time to figure out what gets lost with Erlang is:
a) Ubiquity — everything understands HTTP. Erlang nodes only talk to Erlang (or a compatible runtime)
b) Because there are no middleware standards (REST, GraphQL, OAuth, etc.), you must build or integrate your own abstractions
c) Giving up infrastructure (reverse proxies, load balancers, CDNs), You handle distribution yourself or through OTP design
d) Interoperability with browsers and APIs, requiring bridging via something like Cowboy or gRPC gateway
setcookie secret in Erlang does not create or use an HTTP cookie, SSL certificate, or HTTPS connection. It sets the Erlang distribution cookie, a shared secret string used for node authentication within the Erlang runtime system (the BEAM VM).
Erlang’s built-in distributed networking subsystem allows them to connect to each other if:
1) They can reach each other over TCP (default port 4369 for the EPMD — Erlang Port Mapper Daemon — plus a dynamically assigned port for the node-to-node connection).
2) They share the same cookie value (here "secret").
The author's insight "No HTTP. No REST API", reframes the reality that Erlang moves things down the ISO OSI model - HTTP being on layer 7 and TCP being on layer 4. Erlang therefore misses the "benefits" of operating on a higher ISO layer, but gains powerful advantages of operating on layer 4:
i) True concurrency
ii) Transparent message passing
iii) Fault tolerance
iv) Soft real-time guarantees
v) Persistent cluster connections
Erlang’s design assumes a trusted, closed system of cooperating nodes, not a public Internet of clients. In other words, Erlang doesn’t live below Layer 7 — it IS its own Layer 7.
> a) Ubiquity — everything understands HTTP. Erlang nodes only talk to Erlang (or a compatible runtime)
OTP includes an http client and server. And ERTS includes an http mode for sockets. You may prefer 3rd party http servers (yaws and Cowboy are popular) or clients, but you have options that come with Erlang.
[No comment on b; I'm not sure I aprechiate the concept of standardized middleware]
> c) Giving up infrastructure (reverse proxies, load balancers, CDNs), You handle distribution yourself or through OTP design
You can put all this stuff between the users and you Erlang cluster. Within your Erlang cluster, I don't think it makes sense to leave the cluster and go back in... If you had a large server process in [language of choice], you probably wouldn't send request out to a load balancer to come back into the same process. If you have an n-tier system you may use a load balancer for requests to the other tier... In Erlang, the simplest analog is processes that serve the same request queue would join a pg group, and processes that want to send a request send to one of the members of the group.
> d) Interoperability with browsers and APIs, requiring bridging via something like Cowboy or gRPC gateway
If you want to talk http, you need something that talks http; there's at least 3 reasonable options, plus an http parser for sockets so you can do it yourself without as much fiddly bits. I guess I don't understand what you're looking for here.
This reads as if it isn't trivial to have an HTTP API for your public API in Erlang/Elixir, which is weird. Sure there isn't an included HTTP API for Erlang processes, but why exactly would you want one? They're not for the public internet, as their an implementation detail of your system. The majority of what they're capable of just isn't relevant to the public internet.
Unfortunately very little is trivial for me. Personally I have found the real value of Erlang to be internally between trusted nodes of my own physical infrastructure as a high-level distributed "brain" or control plane for health monitoring, config distribution (env vars, static config files, etc), smart failover decisions etc. Keep the “outside view” (HTTP, SMTP, DNS) all standards-based OSI, internally mapped to daemons each of which is individually robust (HAProxy, MySQL Cluster, Apache/Node.js, Postfix, PowerDNS etc.). Then use an Erlang/Elixir service as a live config and state authority, replicating state across infrastructure, pushing updates in real time, and having my legacy PHP/Python/JavaScript/etc code query this config via a simple HTTP/JSON API into the Erlang service. I'm not all the way there yet, but what works is most encouraging.
This stands to reason. If you need to bridge different languages together like in your case, they need to speak a common tongue. REST/GrahQL/gRPC solve this problem in different ways. There is no technical limitation keeping you from serving HTTP traffic from Erlang/Elixir, but from my own experience it isn't a pleasant experience. JavaScript or Python are dead simple, until you realise that 64-bit integers are not a thing in JS, and need to be handled as strings. Similarly, tuples will give you hell in Python.
On the other hand, if you don't need to cross that boundary, the BEAM will very happily talk to itself and let you send messages between processes without having to even think about serialisation or whether you're even on the same machine. After all, everything is just data with no pointers or cyclic references. That's more that can be said for most other languages, and while Python's pickle is pretty close, you can probably even share Erlang's equivalent of file descriptors across servers (haven't tried, correct me if I'm wrong), which is pretty insane when you think about it.
> I have found the real value of Erlang to be internally between trusted nodes of my own physical infrastructure as a high-level distributed "brain" or control plane
I think this is pretty high praise, considering it's about as old as C and was originally designed for real-time telephone switches.
> There is no technical limitation keeping you from serving HTTP traffic from Erlang/Elixir, but from my own experience it isn't a pleasant experience.
I would be interested in what was unpleasant? I've run inets httpd servers (which I did feel maybe exposed too much functionality), and yaws servers and yaws seems just fine. maybe yaws_api is a bit funky, too. I don't know the status of ACME integration, which I guess could make things unpleasant; when I was using it for work, we used a commercial CA, and my current personal work with it doesn't involve TLS, so I don't need a cert.
> you can probably even share Erlang's equivalent of file descriptors across servers (haven't tried, correct me if I'm wrong)
Ports are not network transparent. You can't directly send to a port from a different node. You could probably work with a remote Port with the rpc server, or some other service you write to proxy ports. You can pass ports over dist, and you can call erlang:node(Port) to find the origin node if you don't know it already, but you'd definitely need to write some sort of proxy if you want to receive from the port.
Perhaps I was a little harsh, this was a few years back when I was evaluating Elixir for a client, but ended up going back to a TS/Node.js stack instead. While the Phoenix documentation is stellar, I found it difficult to find good resources on best practices. I was probably doing something stupid and ran into internal and difficult to understand exceptions being raised on the Erlang side, from Cowboy if I recall. In another case, I was trying to validate API JSON input, the advice I got was to use Ecto (which I never really groked) or pattern match and fail. In JS, libraries like Zod and Valibot are a dream to work with.
The result was a lot of frustration, having been thoroughly impressed by Elixir and Phoenix in the past, knowing that I already knew how to achieve the same goal with Node.js with less code and would be able to justify the choice to a client. It didn't quite feel "there" to pick up and deploy, whereas SvelteKit with tRPC felt very enabling at the time and was easily picked up by others. Perhaps I need another project to try it out again and convince me otherwise. Funnily enough, a year later I replaced a problematic Node.js sever with Phoenix + Nerves running on a RPi Zero (ARM), flawless cross-compilation and deployment.
No, they aren't. You have to use BigInt, which will throw an error if you try to serialise it to JSON or combine it with ordinary numbers. If you happen to need to deserialise a 64-bit integer from JSON, which I sadly had to do, you need a custom parser to construct the BigInt from a raw string directly.
In case you didn't already know of it; CloudI is a cloud framework built with Erlang providing many of the features that you mention - https://cloudi.org/ See the FAQ for overview.
Incidentally, it makes Erlang-built systems robust. I used to run yaws-based web servers (and still do). One laughs at the logs, the feeble adversarial attempts to run this exploit or that. Nothing fits, nothing penetrates, nothing is even remotely relatable.
This is such a conflicting comment for me because I agree with so much but also have so many quibbles. That said I think that the other comments cover most things, I'll just comment on b: I don't think this is a problem that a language should solve or needs to solve, since there is a new flavor of the week of network protocols every few years. off the top of my head
- REST (mentioned, but what kind of REST? Rails style REST? Just plain http resource endpoints?
- GraphQL (mentioned)
- gRPC
- SOAP
- JSON-RPC
- Thrift
- CGI (ok not really in the same category as the above)
- Some weird adhoc thing someone created at 3am for "efficiency"
> a) Ubiquity — everything understands HTTP. Erlang nodes only talk to Erlang (or a compatible runtime)
You're sort of confusing the purpose of Erlang distribution, so I would turn this on its head with the following questions:
Do the Python, Ruby, FORTRAN, Lua, etc, etc, runtimes provide built-in, standardized, network-transparent RPC, [0] or do you have to roll your own using some serialization library and network transport library that might not be The Thing that everyone else who decided to solve the problem uses? Do note that rolling your own thing that is the "obvious" thing to do is still rolling your own thing! Someone else might make a totally reasonable choice that makes their hand-rolled thing incompatible with yours!
I think this confusion influences the rest of your list. Erlang has several HTTP servers written for it [1], but it is not -itself- an HTTP server, nor does it use HTTP for its network-transparent RPC, service discovery, and monitoring protocol.
> The author's insight "No HTTP. No REST API", reframes the reality...
With respect, you're confused. The authors insight can be reworded as
> I can do RPC with no hassle. With just three symbols, I say 'Send this data over there.' and the runtime handles it entirely automatically, regardless of whether 'over there' is running in the same Erlang VM as the calling code or on a computer on the other side of the globe. With equivalent ease, I can say 'When someone sends me data, call this function with the transmitted data.' and -again- the runtime handles the messy details.
When I was first introduced to Erlang's distribution system, it also took me quite a while to detangle it from what I knew about the Web World. Reading through the 'Distribunomicon' chapter of Learn You Some Erlang [2] helped to knock those erroneous associations out of my head.
[0] ...let's not even talk about the service discovery and process/node monitoring features...
[1] Some of which totally do support middleware gunk.
[2] If you've not read LYSE, do note that it was first published in 2010 (and got an update in 2014 to cover the then-very-new Map datatype). Because the folks who work on Erlang tend to think that keeping old code working is a very virtuous thing, the information in it is still very useful and relevant. However, it's possible that improvements to Erlang have made some of the warnings contained within irrelevant in the intervening years.
There's probably not as much advantage to HTTP as you think.
The simplest RPC protocol is where you connect to a TCP socket, send a newline-terminated string per request, and get a similar response back. You don't need HTTP for that - you might still want to use JSON. What does HTTP give you in addition?
It's presumably still not something Erlang directly supports.
HTTP2 offers lots of nice features like stream multiplexing and socket re-use. I guess also encoding standards? Less of an issue in this day and age where everything can work with utf-8.
Presumably the fact that you can interoperate with other systems not part of BEAM is desirable too.
You also get this from newline-delimited-request-response. Multiplexing: send an ID with each request, and return the same ID in the response. Socket re-use: just keep reading until the client closes the socket. Encoding standards: you're the one designing it, so just say it's always UTF-8.
Lower layers of the protocol stack ossify faster in our minds than in reality.
It's a Galapagos island full of stuff that evolved independently.
Concurrency is important, but if you are not familiar with concurrency primitives like mutexes, condition variables, barriers, semaphores, etc. and skipped directly to the actor model, that's a bit like you care a lot about concurrency while not caring at the same time.
Functional programming is great, but your CPU has registers and the way it works is closer to the imperative paradigm.
His PhD thesis explains the thinking behind Erlang, especially how it handles failures, message passing, and concurrency. It was last updated in 2003, 22 years ago, time really flies!
It is a fantastic read. I find myself quoting section 5.3.1 on Well-Behaved Functions (WBFs) quite often, especially rule 2: "If the specification doesn’t say what to do raise an exception."
This really helps systems from muddling along into bizarre states, where things are going awry and nobody knows why.
I discovered Erlang from Bruce Tate's book[0] and it was such an obviously pragmatic and interesting language that I started participating in the Twitter Erlang community, discovered Basho was using it for Riak and looking for a tech evangelist in the Midwest, and thus began my all-time favorite job.
I'm still more of a infrastructure guy than a software developer, but working with such incredibly smart people was a delight. Basho was good at hiring people who could learn Erlang (and, perhaps unsurprisingly, was almost entirely remote).
I can totally relate to this. Programming in Erlang felt so natural compared to the knots I was twisting myself into writing C++. I was churning out C++ code, but wasn't having fun. Suddenly Erlang made it fun and programming became addictive.
> Suddenly Erlang made it fun and programming became addictive.
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?
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.
It’s a subjective thing, but when a language clicks for you, work becomes pleasant. You get into (and maintain) a flow state. It feels as if you’re crafting something beautiful and elegant and simple. It’s the same reason people love painting, drawing, or sculpting with one specific medium over an other. They just like the aesthetic and process better.
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.
It's a bit like cutting wood. Cutting wood can feel terrible. You do it every day because you have to. Along the grain, in wet wood, with a handsaw.
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.
Solving problems I guess. Have you used painful languages before? Imagine doing that, then discovering one that wasn't getting in your way all the time. It's easier to do things that are difficult in other languages. You can do so much on the BEAM, and you don't have to waste your time with thread pools or other nonsense.
For me, strictly immutable and side-effect free programming was a forcing function* to make me really decompose problems into isolated parts. Only when I could do that could I truly say I understood the problem (and its distinct sub-problems) in full.
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.
Every coder has parts of programming they love, and parts they hate. Additionally, they have a mental model of what is risky, and what isn't. Languages, by their design, will make some things easier and some things harder, and so when people "love a language," they typically mean "it maps to what gives me dopamine."
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.
Everyone talks about GenServers and OTP and all that, and that part is great, but what sells it for me is that it's a practical functional programming language. You get 80% of the FP part of Haskell but you don't have to mess with monads. It has tail call optimization which makes it easy to write functions in general, one "function" is made up of several that call each other, much easier to reason about and write. In general code I use what I call the "holy trinity", atoms, tuples and pattern matching.
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.
The ability to load recompiled code on running processes makes it possible to change live services on the fly. I worked for a low budget mobile operator in SE-Asia where there were no test platforms, and every change was on the live servers. Erlang made changes fast, as small changes were trivial, while large changes were broken down into small changes, implemented individually, and tested. Rolling back was fast, as I kept the original code before modifying. No convoluted build structures, and praying the rollout to production doesn't throw unexpected errors.
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.
when I program, I am visualizing the machine I am building in my head. some language semantics are cleaner than others, allowing the bits and pieces to flow together more naturally, composing in elegant ways that do not require pointless effort be spent manually wrangling footguns and minutia.
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.
For me, it was Elixir pattern matching + GenServers + SupervisionTrees + Let It Fail mentality.
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.
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.
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.
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.
Yep; the submitted article is just pedestrian with lots of posturing and pretty language to sell it. There is nothing of Programming/Mathematics/Erlang in it to deserve the upvotes.
Some of the comments here are far more informative.
It's funny, because I find Erlang to be one of the least accessible languages I've ever tried (and my entry into this world of digital wonders was thru disassembling cracktros on a M68k, before internet, in a then-foreign language).
That said, the metaphors are so elegant, the key concepts so well chosen -- yes, the initial onramp may be a cognitive slog, but it's well worth it. It makes everything downstream so much easier.
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.
> 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.
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.
I think what has changed mainly is that today we have tools, languages and entire ecosystems that exist only as means to support someone’s product line.
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.
When I was working with it (I was there, 4000 years ago) there was some talk about Swift for the server, but neither obj-C nor Swift ever really breached containment of the Apple ecosystem and -tooling. Which is a shame because at the time I enjoyed working in XCode. Who knew using a mouse swipe to go back in your code would be so natural? Not any other IDE developer, ever.
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.
That is why I don’t like those ecosystems. They’re all relying on magic (code generation and indexing) for everything instead of just providing a good notation.
If you’re creating that closed of an ecosystem, at least learn from history and create something like smalltalk.
Yeah, Swift was born because Apple had an ecosystem and they needed to fill it with a language. Erlang was born because Ericsson had a problem and they needed to solve it with a 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.
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.
>somehow almost aggressive/corporate style even when there is no paid product.
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.
GNU Guix has a good blog, but I don't feel like they are very "marketing" focused.
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 think this is especially true of projects run by people with deep experience in the field and are in it “for the love of the game”, and don’t feel the need to stunt on everyone in hopes that they are taken seriously.
I think it's because we are wired that attention is it's own currency nowadays. And it's also true. Even if there's no paid product, you get strength in numbers. If you depend on an open source library, it's usually better for you if others depend on it too.
Certainly an element of that but there are also cases where the superior product "lost" to the inferior product because the inferior one was better marketed.
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.
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.
What were the major projects that changed that? Rails and Prototype.js feel like early examples where there was a major cult of personality around their creators, but I don't know if that was really unique to them or something that was just an extension of the webdev / css / web standards blogger culture of the era— A List Apart and all the people who would got to SXSW every year to chit chat with each other on panels and share the hottest new way to make a row of tabs without using a <table>.
I don't think it's cycles, more like newcomers rediscovering the future.
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.
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.
> Why has no one put microkernels and Erlang into a blender? I know there's QNX, but it's still UNIX, not Erlang.
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.
You nerd sniped me a little and I'll admit I'm not 100% sure what a reduction is but I've understood it to be a measurement of work for scheduling purposes.
Oh that's a really neat find. I'm not sure how 'instructions' map to 'reductions' in the sense that if you stop when a reduction is completed the system is in a fairly well defined state so you can switch context quickly, but when you stop in mid reduction you may have to save a lot more state. The neat thing about the BEAM is that it is effectively a perfect match for Erlang and any tricks like that will almost certainly come with some kind of price tag attached. An interrupt is super expensive compared to a BEAM context switch to another thread of execution, you don't see the kernel at all, it is the perfect balance between cooperative and preemptive multitasking. You can pretend it is the second but under the hood it is the first, the end result is lightning fast context switches.
But: great find, I wasn't aware of this at all and it is definitely an intriguing possibility.
> That would be an absolutely awesome project. Usually when you can think of it someone has already done it so a bit of googling
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?
I've got a hobby OS you may want to check out. Crazierl is a just enough kernel that can run the FreeBSD build of BEAM as a single OS process.
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.
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.
I got distracted (story of my life) by something very interesting that takes precedence but I'll return to it at some point, I've made up my mind about that.
v86 has been posted a few times [1], I'm pretty sure that's how I became aware of it to use as a target platform. It's a nice project, and they've taken three PRs from me (so far) that make it easier for me to run my weird OS :) Lots of other contributions here and there from others which is great, because I thought I was going to have to build a virtio-net device in v86 or write a ne2k driver in crazierl, and other people built and debugged the virtio-net so I can just use it.
> Any reason you haven't posted it on Github/Codeberg?
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.
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
FP language is very hard on novice programmers. You can write thousands of lines of bad javascript/java/python code, but you won’t write ten in FP without the whole thing blowing up.
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.
> 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?
I work with elixir daily and I would concur. elixir's semantics line up nearly 1:1 with the clojure code I used to write a few years ago. Its basically if you replaced the lisp brackets with ruby like syntax. The end result is a language that is much easier to read and write on the daily with the disadvantage of making macros more difficult. I would argue that it should be difficult since you should avoid using it until absolutely necessary. Lisps on the other hand, practically beg you to use macros as the entire language is optimized for their use.
It’s perennially in my list of languages to check out. It felt harder when I looked into it 15 years ago. Now that functional programming is second-nature, it should be much easier.
> Lets count the number of comercial sucesful released games that don't use mutability or object orientation....
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.
Oddly enough, you can write in a functional style in most any programming language. It's true!
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:
What sort of applications is Erlang particularly suitable for?
Distributed, reliable, soft real-time concurrent systems.
...
What sort of problems is Erlang not particularly suitable for?
...
The most common class of 'less suitable' problems is characterised by performance being a prime requirement *and* constant-factors having a large effect on performance. Typical examples are image processing, signal processing, sorting large volumes of data and low-level protocol termination.
...
Most (all?) large systems developed using Erlang make heavy use of C for low-level code, leaving Erlang to manage the parts which tend to be complex in other languages, like controlling systems spread across several machines and implementing complex protocol logic.
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]
Erlang is no silver bullet and will be particularly bad at things like image and signal processing, operating system device drivers, etc. and will shine at things like large software for server use (i.e.: queues, map-reduce), doing some lifting coupled with other languages, higher-level protocol implementation, etc. Areas in the middle will depend on you. You should not necessarily wall yourself in server software with Erlang: there have been cases of people doing unexpected and surprising things. One example is IANO, a robot created by the UNICT team, which uses Erlang for its artificial intelligence and won the silver medal at the 2009 eurobot competition. Another example is Wings 3D, an open source 3D modeler (but not a renderer) written in Erlang and thus cross-platform.
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?
[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.
It's funny that you mention this, and it made me take some time to appreciate I've been working with Elixir full-time for almost 10 years now, and the entire experience has been so... stable.
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.
For me it took a tremendous amount of work to somewhat understand the OTP stuff though.
Its one of those languages where I can never be confident about my implementations, and thankfully it has features to check whether you have stale processes or whatever.
A language I am humbled by whenever I use it.
Here's a trick to confidence in a BEAM system. If you get good at hot loading, you significantly reduce the cost of deployment, and you don't need as much pre-push confidence. You can do things like "I think this works, and if it crashes, I'll revert or fix forward right away" that just aren't a good fit for a more common deployment pattern where you build the software, then build a container, then start new instances, then move traffic, etc.
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.
Erlang's hot reload is a two-edged blade. (Yes yes, everything is a tradeoff but this is on another level.)
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.
You certainly can forget to push fixes to the source repo. But if you do that enough times, it's not hard to build tools to help you detect it. You can get enough information out of loaded modules to figure out if they match what's supposed to be there.
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.
That's part of it yeah. But, at least in my experience, that tells me you pushed code (to disk) and didn't load it. You could probably just notify at 4 am every day if erlang:modified_modules() /= []; assuming you don't typically do operations overnight. No big deal if you're doing emergency fixes at 4 am, you'll get an extra notification, but you're probably knee deep in notifications, what's one more per node?
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.
Thank you for this post and I'll add a note for people who are seeing this and are maybe discouraged about learning Erlang/OTP/Elixir.
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.
I love saying this but OTP is a really roughneck standard library. They just added shit to it as they needed it without apparently putting too much consideration into the organization, naming, or conventions.
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.
It also took me quite a bit of time to understand OTP. In fact, I had to have a project that actually required what OTP offered to really get it.
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.
1/3 of hn posts (maybe more) are "look at this thing we built!" or a combo of that with "plus all this VC money!" where op has basically re-invented something that has existed in Erlang since forever. I don't mind all these cool new things (it's why I visit). But personally, I prefer to cut to the chase and just use Erlang (well, in my case - Elixir).
Feels like Rust has stolen a lot of Haskell thunder, in the sense you can write similar code and satisfy that functional programming itch in a much more popular language, while falling back on imperative programming if you really need it.
I haven't been following closely, but my impression is that the dependently-typed languages/theorem-provers have stolen some of Haskell's "most purely functional, most strongly typed" hype-thunder even though they're even further from being a mainstream implementation language for most projects.
Unfortunately, I have to disagree on the absolute notion, that you will never see someone being not nice in the Erlang world. OK, you technically said "behind Erlang", not "in Erlang ecosystem". But in the Erlang ecosystem and related languages I've had at least 2 encounters, that were not nice. One outright called lack of good documentation for some projects in the Erlang ecosystem bullshit in a public chatroom, and it killed all my motivation to continue exploring that part of the ecosystem, the other one probably without knowing gave low effort dismissive responses to questions about how to best do something, without even knowing the context in which my code appeared, mixing in their personal taste and treating me like a little child.
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.
> X equals X plus one? That’s not math. That’s a lie.
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.
I highly recommend How to Design Programs. I recall being repeatedly mind blown working through the book. It was great fun. The authors start by composing pure functions. IIRC you get quite far before you have to do any mutation. Take a look! https://htdp.org/2003-09-26/Book/curriculum-Z-H-5.html
Mutation should be an advanced topic in programming teaching. Even in procedural languages. It should be seen as an optimization technique you only use when analysis has shown that to be the only way to solve a difficult bottleneck. Using mutation as a basic tool in programming was a mistake.
That's a funny thing to get hung up on, though I can understand it. I suppose that's the downside of some programming languages going with the worst assignment operator. If they didn't they could use = for (numerical) equality tests. If your wife has a strong mathematical background you could also show her some logic programming like Prolog or Mercury.
This blocked me from understanding computer programming for 3 years. I thought of 'variables' as immutable and the fact that they were not didn't really click at all. Once I mentally transitioned from 'it's a label attached to a value' to 'it's a named box which holds a value' life got a lot easier. I was 15 when it finally clicked (on a TI programmable calculator...).
The answer to that is surprisingly simple: no. Because Haskell didn't exist at the time. Neither did Erlang. But LISP did, but was just too expensive and memory hungry for mere mortals such as me. I had to learn all this on a shoestring budget working an 8 hour job and barely having left over enough money for food after rent, taxes and insurance. It took a long time before I was able to afford a floppy drive, much less a half decent computer, one that would be able to run LISP. And those language packs weren't cheap either and open source wasn't really a thing yet (but shareware actually was).
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.
I learned to program in the 8 bit era, initially in BASIC on the TRS 80, then later assembly on the KIM-1 and then the 6809 based Dragon 32 (CoCo clone), which for the first time felt like a real computer. After that the BBC Micro and then the ARM, and eventually PCs.
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.
Another series that really helped me was Leventhal's assembly language series. The problems in the books were relatively simple but they gave you a hold on one CPU if you already knew something about another and they served as instruction set reference as well (which were ridiculously hard to get here in Europe, and I don't mean those little cards but the full data sheets).
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.
> She found it very frustrating [...] I wonder if I should try a [different language] with her
Or, perhaps, preprocess left-arrow to equal?
Customizing a language to taste. Especially for shallow syntactic issues. Like "no multi-line string literals allowed ... I'm sorry, that's just not ok". Or "ok kid, what nicknames would you like to give the keywords today"?
When programming for just fun, I'll sometimes go "this aspect of the language bugs me - I could of course just live with it... but here, I think it will be more fun not to". So for recent example, a few lines of recast.js ast tweaking, and I could use infix unicode mathematical symbols as javascript functions.
I wonder if one could teach programming by build-your-own language. Perhaps even of the video-scanned desktop variety. "Draw a circle around your toys to create a collection. Label it with a letter. Now you can sort/filter it by..."
I think it makes more sense in languages where you use the "let" keyword. Then it sounds like assignment, though you still have to get comfortable with the X being on the right side too.
Although in Erlang it's closer to an equation with assignment being a side effect. `=` is the pattern matching operator.
Eshell V15.2.6 (press Ctrl+G to abort, type help(). for help)
1> X = 1.
1
2> X = X.
1
3> 1 = X.
1
4> X = 2.
** exception error: no match of right hand side value 2
5>
That's exactly right. Looking at the Assignment Operator Wiki page, it's also clear where these notations come from.
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.
It’s not really such a different meaning, it’s just shorthand for something like:
X’ = X + 1
DELETE X
X = X’
Even if you treat it as “storing a value in a slot”, a comprehensive model of the operation needs to model the fact that the value of the slot changes over time. So another way to look at it would be:
X_t1 = X_t0 + 1
…except that “X” is shorthand for “the latest binding that uses this name.”
I explain "X equals X plus one" as though each variable has an implicit version that increases whenever it's written to or as time goes on, but we can only access the lastest version.
Tell her there are transparent subscripts on each use of =.
x(0) = 1
x(1) = x(0) + 1
There's no way to reference the subscripts. They're implicit and the language manages it for you. Ask her to think of the Kronecker delta function and it's definition.
I think it's mental rigidity. In BASIC, the = symbol means both assignation as in
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.
Am I wrong to be skeptical that this was a thought author had at 8 years old? I don't doubt that 8-year-olds can learn programming and advanced math, but even the brightest 8-year-old would still be in the "sponge" stage. It's hard to believe they would have a sufficiently fixed idea about algebra to be troubled that X = X + 1 is "wrong".
Kids can be flexible but also rigid about the most random things. My two year old gets really annoyed if I use the wrong spoon to stir my tea, saying certain spoons are only for food or cereals. I don't know where she got that – it's definitely not a practice we ever enforced!
I've shared it here once recently, so might as well again.
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.
Pascal also had := for assignment, if I remember correctly. I disliked it, to be honest, = is pretty much universally accepted in the IT world to mean "assign to".
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.
> I don't think this is something that keeps someone from programming.
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.
When all you know is hammers, all you can think of are nails. Screws are impossible to use and understand. How are you supposed to hammer in a screw? All those ridges are in the way!
The hyperbole that Haskellers invoke around pure-functional vs impure is that you're never quite sure whether a function call you make will "fire the missiles".
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:
(X = X + 1) is bad
to
(mailbox = mailbox + message) is so simple!
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).
In Haskell with `head (sort list)` the entire list does not have to be sorted, depending on the sort implementation. Everything is lazy, so sort can sort the list just enough to return the smallest element.
Going beyond laziness, a compiler that can understand and exploit equations, could use `head (sort list) = smallest (list)` to make the program more efficient, going from O(n * log n) to O(n) complexity.
> The author did not say this at all, they barely even touched on capabilities of erlang/OTP.
Two separate Erlang nodes. On different machines, different networks, different continents if I wanted. And they could just… talk. No HTTP. No REST API. No serialization headaches. Just message passing. Just actors doing their thing.
> Their focus was on the functional syntax of Erlang.
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?
Right. The author barely touched on the capabilities of Erlang/OTP. That section was pretty much a demo of the syntax and an advertisement of the fact that the language syntax and runtime system makes it trivial to have a distributed program that runs on separate machines.
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.
My point is, for an article about functional programming and Erlang, there wasn't any functional programming in Erlang.
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.
> there wasn't any functional programming ...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.
> [ping and pong] only exist for their side effects.
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.
I think the question is whether sort should return a new, sorted array or whether it should sort the array in place. In functional languages it is the former, in imperative the latter.
It can be quite useful to have nondestructive sorting in imperative languages as well. Hence python introducing 'sorted' even though '.sort()' preceded it.
Unrelated to the topic, but I really like the feature `> cd ..` at the bottom of the article. It's simple and accessible even with a smartphone. I wonder if there is an easter egg inside.
Because the homepage has this "interactive terminal" which is funny and nice (kudos for readline shortcut support) but also partially "broken", i.e. the blog post(s) and other links you see with "ls" are not clickable, at least on Firefox.
Thank you so much for the write up! I've been avoiding processes for some reason while I've been learning Elixir and this post pushed me to play with it today. And I started in Erlang and then managed to translate it to Elixir successfully!
I ran into this issue and I'm not sure if it's just a me thing.
When sending the message from the Pong REPL, the following didn't work for me:
I used Erlang for a couple of years. It's cool and elegant for sure, but it always felt like it took longer to write than JS or Py like I'd otherwise use. Recursion with matchers is sometimes natural, sometimes roundabout to what you want to do.
The multiprocess stuff is cool too, but a premature optimization for what I was doing. If I needed to scale, I don't know if that'd be the chosen approach.
I do a lot of Elixir programming, which is different and not at the same time :-) . What I find is that it takes me longer to write the initial code than I might in, say, JavaScript, but that the refactoring and ongoing maintenance of code is significantly faster. This is because I think the functional & actor paradigms force you to think harder upfront, but once those decisions are made are easier to manipulate. For me personally, I'd take that trade since I'm either refactoring or maintaining much more frequently... but I understand there are other considerations which could drive a different preference.
Also, I'm sure my personal experience isn't necessarily true for everyone... so weight this anecdote accordingly.
A few things to keep in mind. This is a "labor of love" personal project... sure a large one... but that's what it is. Another thing to consider is that I go off standard Elixir practices in a number of different ways and for reasons™; in this sense the project isn't representative of what a typical Elixir project looks like.
When I say that things take longer for me to initially code, I'm not including "self-inflicted" time dealing with novel issues which arise from taking a less common path... which does have its own time cost. In my previous comment I'm only really thinking of the typical aspects of Elixir development time.
Finally, I do need to emphasize: for me the cost is worth it. Once I get to maintaining that now-developed code or doing refactors, so long as I'm not throwing away everything and starting from scratch, the development time for me is rather less than I have with code of similar complexity developed in other languages. All personal experience... but there you have it.
One of the features I really like about the BEAM is that it solves the problem of organizing the topology of the applications I build. It's unlikely you're even close to doing this in other languages / frameworks.
The supervisor tree is one of the killer-app features of BEAM. I hit the benefits of this ALL the time. I notice how it keeps getting solved over and over again in unique ways that are half-baked. In some ways, this quote is what I'm hinting at, except, things don't even have to be concurrent. That's just a cherry-on-top.
"Virding's First Rule of Programming:
Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified bug-ridden slow implementation of half of Erlang."
I'd also argue that "sufficiently complicated" bar isn't very high.
There are matured ways to do multi-service outside of BEAM. I get that it's extra work vs something built in, but this is only an issue at large scale, and you'd probably need to set that up anyway unless every service is Erlang/Elixir.
That's the downside. If you look at the monthly "who is hiring" posts here on HN, you rarely see Erlang jobs. Elixir shows up a bit more often but it's still uncommon.
Only had one, a contract where I got to choose the implementation. Job was to simulate a bunch of sensors on a network and Erlang was perfect for that.
> even in the sum example, you see the statement "N is n-1"
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.
It is difficult to understand the full beauty (or horror, depending on your pov) of functional programming with such a simple example. But as you scale up in complexity, it can be someone’s full time job to make sure the model is accurately translated to the computer’s memory. With a pure language, the compiler is responsible for maintaining the mapping to memory, so you (mostly) just focus on the symbolic relationships of your model. This can be extremely freeing once you train yourself to think in functional terms. Of course, there is a performance cost, but this can be managed and in cases where people turn to functional programming reliability and correctness outweigh the cost.
Pure languages still use stack to store mutating state. In a single-threaded programs this is almost non-observable except for occasional stack overflow crashes. But with multiple threads and message passing one can emulate arbitrary state mutation just by using messages and stack.
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.
Calls a simple swap function with pointers inscrutable (the only hard to decipher character is the asterisk), and as a good counterexample provides this ?
Like other folks have said, show the equivalent C program and then we can have a discussion about implementation complexity and comprehensibility.
Do note that the equivalent C program permits IPC between two instances of the program that could be either running on the same machine or could be running on different machines connected by an IP network.
Edit: For general information, here's an Erlang implementation of increment and swap. Swap is made a bit complicated because Erlang doesn't support multiple returns... returning a tuple or list is the way that's handled, so I had it accept a two-element tuple for consistency with what it returns:
Decades long C programmer here. C is hard to parse. The thing is, you get used to it and then it feels like it is normal, but it really isn't. Not as bad as Perl, but pretty bad. And it is easy to construct one liners that are tricky to understand, I think C is the only language that has an obfuscation contest that keeps turning up gems year after year.
For what it's worth, I'm stuck on the very first x = x + 1 thing.
Not sure if you want to call it a screwup or bad grammar or whatnot, but it is perhaps the huge mistake that the "equals" sign was used for something that feels like, but emphatically DOES NOT mean, "is equal to."
It's "put this into that". It's an action verb. Should have perhaps insisted on x <- x + 1 or maybe better x + 1 -> x
The difference is that it is an instruction. Conventional mathematical notation, while declarative by default, switches into instruction mode just the same with the "let" keyword. The usage of the "=" operator then becomes equivalent: e.g. let x = 1.
But as the aforementioned x = x + 1 comes from notations that are never declarative, where every statement is an instruction, the "let" keyword is unnecessary and redundant. You already know you are looking at an instruction.
> Should have perhaps insisted on x <- x + 1 or maybe better x + 1 -> x
While that would obviously work, it strays further from the conventional notation. Which doesn't matter to the universe in any way, but the topic of discussion here is about trying to stay true to conventional notation, so...
I do think what I'm trying to say here is: sorry, but your "conventional notation" sucks because "what is actually happening" is so very different from how the thing is overwhelmingly used for most people.
Yes. "=" doesn't mean anything in the void of space. Conventional mathematical notation is what established "=" as meaning "equal to", as referenced by the original comment. But the same notation also uses it for assignment when in instruction mode, so imperative languages that use x = x + 1 syntax are quite consistent with it.
> but your "conventional notation" sucks
Maybe, but it's all anyone really knows nowadays. It's what you are going to learn in math class in school. It's what you are going to find used in mathematical papers. It is how you are going to express mathematical concepts to your friends and colleagues. Worse is better, I suppose, but it is what has set the standard. It is the de facto language of math. For whatever shortcomings it does have, virtually everyone on earth recognizes it, which is very powerful.
> so very different from how the thing is overwhelmingly used for most people.
I'm not sure how to grok this. let x = 1 is something anyone who has taken high school math will have encountered. Assignment is perfectly in line with the understandings of most people.
Do you mean that expression entirely using imperative constructs is unfamiliar to those who grew up with a primarily declarative view of math? That might be fair, but I'm not sure x = x + 1 is a specific stumbling block in that case. One has to understand imperative logic in its entirety to use these languages anyway, at which point nobody is going to think that x = x + 1 is intended to be declarative equality.
I believe you can blame Ken Thompson for this. In DMR's paper about early C history, he says:
> Other fiddles in the transition from BCPL to B were introduced as a matter of taste, and some remain controversial, for example the decision to use the single character = for assignment instead of :=.
I think Ken did most of the early design of B and DMR came along later to help with C. Ken has a famously terse style, so I can definitely see it being on brand to shave of a character from `:=`, which is what BCPL uses.
It's sort of a tricky little syntax problem. It makes perfect sense to use `=` for declarations:
int x = 1;
At that point, you really are defining a thing, and `=` is the natural syntax for a definition. (It's what BCPL uses for defining most named things.)
You also need a syntax for mutating assignment. You can define a separate operator for that (like `:=`). Then there is the question of equality. From math, `=` is the natural notation because the operator there is often overloaded as an equality predicate.
Now you're in a funny spot. Declaring a variable and assigning to it later are semantically very similar operations. Both ultimately calculate a value and store it in memory. But they have different syntax. Meanwhile, defining a variable and testing two expressions for equality use the same syntax but have utterly unrelated semantics.
Given that math notation has grown chaotically over hundreds of years, it's just really hard to build an elegant notation that is both familiar to people and consistent and coherent.
Look, I teach IT (to both novices and interested folks at a college) for a living; count this idea as one that, when I present it this simply, many a lightbulb goes on for those new to programming. Much like for zero-indexed arrays, I do very well with "look, this is stupid, but here's why it got that way, we'll just deal with it."
And while on occasion I take pride in nerd-dom, I feel like -- especially with the advent of AI for coding -- this is dinosaur stuff, where we would do better to go along with what's WAY more intuitive than to try to stick with mathematical or programming purity, etc.
That’s funny, because to me, it was always immediately obvious that once that line runs, then it must be true - once that line runs, x is now equal to whatever it was before, + 1. It’s the price opposite of the lie, it’s the literal definition of truth, in the sense that what is true is set by that line.
Programming is telling things to the computer, and in this case, you’re telling it what x is. Whatever you tell a computer is all the computer knows, whatever a computer does can only be what it’s told. If you never told it what x was, then x wouldn’t be anything… that’s the truth.
This is the key point. Some people have a mental model that algebraic syntax is describing a set of immutable properties. You are defining things and giving them names, but not changing anything. There is no notion of time or sequence, because the universe is unchanging anyway. All you're doing is elucidating it.
Then there is a model where you are molding the state of a computer one imperative modification at a time. Sequence is essential because everything is a delta based on the state of the world before.
You have the latter model, which is indeed how the hardware behaves. But people with a mathematical mindset often have the former and find the latter very unintuitive.
It's hard for me to not suggest though that, essentially -- the math people are "right." Which is to say, "=" meant what it meant for 400 years, and then computers come along and redefine it to be an action verb and not an identity. And I think it's fair to consider that a mistake.
There is also Erlang the Movie 2 trying to sell Erlang (called Outlaw Techno Psyhchobitch in the video) with rock and hot chick: https://youtu.be/rRbY3TMUcgQ?t=262
And they were right, rebranding was all Erlang needed 13 years ago
It is not immediately clear what the intended usage pattern for Lumen and WebAssembly is.
Pros and Cons
Things we like:
The idea of having both runtime and code compile is cool
Things we’re not big fans of:
We could not immediately figure out exactly how Lumen works, and builds appear to be failing
The project might be stalled or unmaintained. It has not been updated in over a year.
WebAssembly’s goal is to be a “portable execution layer” — an OS abstraction.
If WASM becomes the standard runtime across browsers, servers, and edge networks, something still has to orchestrate thousands of concurrent tasks, message queues, supervisors, restarts, etc.
Erlang/OTP already solves that — beautifully.
So, the motivation is:
“What if BEAM’s concurrency runtime could itself be compiled to WebAssembly — and become the actor system of the WebAssembly world?”
That’s why projects like Firefly and Lumen are interesting: they’re exploring whether Erlang’s runtime model can become part of the WASM ecosystem — just like how Go and Rust shaped the serverless world.
A ten-second search reveals [0]. (Have Kids These Days forgotten all about Emscripten?) However, given that web browser pages are often short-lived, I don't see what benefit bringing in all of Erlang and its VM gets you, other than the fact that you've pulled off the stunt.
It depends on how technical you want to get. It's certainly not a loop in the classic sense, like a for loop etc. Like there are no `continue` or `break` semantics and you can't return out of it (and yes yes of course you can still do all that stuff, just not with dedicated keywords). I call it a loop, though. Whenever I make a lil' server in Elixir (mostly always for teaching) I always call the function `loop`:
defmodule Server do
def start_link do
spawn_link(&loop/0)
end
defp loop do
receive do
msg ->
IO.puts(msg)
loop()
end
end
end
Of course, that assumes that you write your functions so that they can be converted into iteration!
Did you know that some C/C++ compilers will do tail-call -er- optimization? I learned this quite a while ago, so I'd expect every major C/C++ compiler to do it by now, but I was pretty impressed at the time.
> Of course, that assumes that you write your functions so that they can be converted into iteration!
I was indeed assuming this, however due to my lack of compiler knowledge, not explicitly writing your functions--at least in the classic way--doesn't necessarily mean you Erlang functions won't be optimized. See section 2.3 [0]
Though again, I don't know shit about compilers and while I can imagine what callER optimized calls could look like, I would need some examples!
> ...while I can imagine what callER optimized calls could look like...
What do you mean by "callER" optimized calls? That's a term I think I'm quite unfamiliar with.
> Though again, I don't know shit about compilers...
Oh, I also know fuckall about compilers. I'm just a bumbler who has muddled through a moderately-successful programming "career".
IME, on the topic of recursive functions, the big difference between Erlang and -say- a C++ program compiled with GCC is that the latter has a specific (but surely configurable somehow) call stack size limit which terminates the program if you exceed it. Whereas Erlang's limit seems to be "Well, how much RAM do you have, and how much space do I need to store away the arguments for each call?".
When I mentioned that some C/C++ compilers do tail-call optimization, what I intended to say was that they converted the recursive call into something more like iteration. I'm pretty sure that historically [0] the only way you could do this optimization is if the very last thing a function did was to call itself... doing anything after that call meant that the conversion to iteration was not possible. I have no idea if things have gotten quite a lot fancier in the years since I made this discovery and now.
If Erlang had a call stack whose size was limited by something other than the available RAM in the system, then whether or not your functions were written tail-recursive [1] would be quite a lot important, I think.
[0] And maybe it's still the case today? Refer back to my near-zero knowledge of compilers.
[1] I hope I don't forget that term again. It's much more succinct than my jumble about "tail-call optimization"
I have been trolling for a while now to find a great project to write in Erlang. I can't write any erlang and it's sufficiently foreign it's been fun to try to get my LLM to do it for me. This is because I cannot make any useful suggestions on _how_ to write it.
I have a toy replacement for syslog.
i supervised an LLM writing a discord-esq chat program that implemented a imaginary JMAP extension called 'JCHAT'.
i had the LLM write a Erlang based dimensional accounting system
So far in real life i don't have any uses, which is a shame because it's a great language and OTP comes so batteries included I can think of many use cases.
Yep. You feed files annotated with those to Dialyzer or similar for not-runtime typechecking. That plus the basic runtime checking provided by guard expressions [0] gives you a bunch of coverage... and I think some of the folks who like Elixir are working on their own typechecker thing? I don't really know, because I don't use Elixir.
Took me a long time to figure out what gets lost with Erlang is:
a) Ubiquity — everything understands HTTP. Erlang nodes only talk to Erlang (or a compatible runtime)
b) Because there are no middleware standards (REST, GraphQL, OAuth, etc.), you must build or integrate your own abstractions
c) Giving up infrastructure (reverse proxies, load balancers, CDNs), You handle distribution yourself or through OTP design
d) Interoperability with browsers and APIs, requiring bridging via something like Cowboy or gRPC gateway
setcookie secret in Erlang does not create or use an HTTP cookie, SSL certificate, or HTTPS connection. It sets the Erlang distribution cookie, a shared secret string used for node authentication within the Erlang runtime system (the BEAM VM).
Erlang’s built-in distributed networking subsystem allows them to connect to each other if:
1) They can reach each other over TCP (default port 4369 for the EPMD — Erlang Port Mapper Daemon — plus a dynamically assigned port for the node-to-node connection).
2) They share the same cookie value (here "secret").
The author's insight "No HTTP. No REST API", reframes the reality that Erlang moves things down the ISO OSI model - HTTP being on layer 7 and TCP being on layer 4. Erlang therefore misses the "benefits" of operating on a higher ISO layer, but gains powerful advantages of operating on layer 4:
i) True concurrency
ii) Transparent message passing
iii) Fault tolerance
iv) Soft real-time guarantees
v) Persistent cluster connections
Erlang’s design assumes a trusted, closed system of cooperating nodes, not a public Internet of clients. In other words, Erlang doesn’t live below Layer 7 — it IS its own Layer 7.
> a) Ubiquity — everything understands HTTP. Erlang nodes only talk to Erlang (or a compatible runtime)
OTP includes an http client and server. And ERTS includes an http mode for sockets. You may prefer 3rd party http servers (yaws and Cowboy are popular) or clients, but you have options that come with Erlang.
[No comment on b; I'm not sure I aprechiate the concept of standardized middleware]
> c) Giving up infrastructure (reverse proxies, load balancers, CDNs), You handle distribution yourself or through OTP design
You can put all this stuff between the users and you Erlang cluster. Within your Erlang cluster, I don't think it makes sense to leave the cluster and go back in... If you had a large server process in [language of choice], you probably wouldn't send request out to a load balancer to come back into the same process. If you have an n-tier system you may use a load balancer for requests to the other tier... In Erlang, the simplest analog is processes that serve the same request queue would join a pg group, and processes that want to send a request send to one of the members of the group.
> d) Interoperability with browsers and APIs, requiring bridging via something like Cowboy or gRPC gateway
If you want to talk http, you need something that talks http; there's at least 3 reasonable options, plus an http parser for sockets so you can do it yourself without as much fiddly bits. I guess I don't understand what you're looking for here.
This reads as if it isn't trivial to have an HTTP API for your public API in Erlang/Elixir, which is weird. Sure there isn't an included HTTP API for Erlang processes, but why exactly would you want one? They're not for the public internet, as their an implementation detail of your system. The majority of what they're capable of just isn't relevant to the public internet.
Unfortunately very little is trivial for me. Personally I have found the real value of Erlang to be internally between trusted nodes of my own physical infrastructure as a high-level distributed "brain" or control plane for health monitoring, config distribution (env vars, static config files, etc), smart failover decisions etc. Keep the “outside view” (HTTP, SMTP, DNS) all standards-based OSI, internally mapped to daemons each of which is individually robust (HAProxy, MySQL Cluster, Apache/Node.js, Postfix, PowerDNS etc.). Then use an Erlang/Elixir service as a live config and state authority, replicating state across infrastructure, pushing updates in real time, and having my legacy PHP/Python/JavaScript/etc code query this config via a simple HTTP/JSON API into the Erlang service. I'm not all the way there yet, but what works is most encouraging.
This stands to reason. If you need to bridge different languages together like in your case, they need to speak a common tongue. REST/GrahQL/gRPC solve this problem in different ways. There is no technical limitation keeping you from serving HTTP traffic from Erlang/Elixir, but from my own experience it isn't a pleasant experience. JavaScript or Python are dead simple, until you realise that 64-bit integers are not a thing in JS, and need to be handled as strings. Similarly, tuples will give you hell in Python.
On the other hand, if you don't need to cross that boundary, the BEAM will very happily talk to itself and let you send messages between processes without having to even think about serialisation or whether you're even on the same machine. After all, everything is just data with no pointers or cyclic references. That's more that can be said for most other languages, and while Python's pickle is pretty close, you can probably even share Erlang's equivalent of file descriptors across servers (haven't tried, correct me if I'm wrong), which is pretty insane when you think about it.
> I have found the real value of Erlang to be internally between trusted nodes of my own physical infrastructure as a high-level distributed "brain" or control plane
I think this is pretty high praise, considering it's about as old as C and was originally designed for real-time telephone switches.
> There is no technical limitation keeping you from serving HTTP traffic from Erlang/Elixir, but from my own experience it isn't a pleasant experience.
I would be interested in what was unpleasant? I've run inets httpd servers (which I did feel maybe exposed too much functionality), and yaws servers and yaws seems just fine. maybe yaws_api is a bit funky, too. I don't know the status of ACME integration, which I guess could make things unpleasant; when I was using it for work, we used a commercial CA, and my current personal work with it doesn't involve TLS, so I don't need a cert.
> you can probably even share Erlang's equivalent of file descriptors across servers (haven't tried, correct me if I'm wrong)
Ports are not network transparent. You can't directly send to a port from a different node. You could probably work with a remote Port with the rpc server, or some other service you write to proxy ports. You can pass ports over dist, and you can call erlang:node(Port) to find the origin node if you don't know it already, but you'd definitely need to write some sort of proxy if you want to receive from the port.
Perhaps I was a little harsh, this was a few years back when I was evaluating Elixir for a client, but ended up going back to a TS/Node.js stack instead. While the Phoenix documentation is stellar, I found it difficult to find good resources on best practices. I was probably doing something stupid and ran into internal and difficult to understand exceptions being raised on the Erlang side, from Cowboy if I recall. In another case, I was trying to validate API JSON input, the advice I got was to use Ecto (which I never really groked) or pattern match and fail. In JS, libraries like Zod and Valibot are a dream to work with.
The result was a lot of frustration, having been thoroughly impressed by Elixir and Phoenix in the past, knowing that I already knew how to achieve the same goal with Node.js with less code and would be able to justify the choice to a client. It didn't quite feel "there" to pick up and deploy, whereas SvelteKit with tRPC felt very enabling at the time and was easily picked up by others. Perhaps I need another project to try it out again and convince me otherwise. Funnily enough, a year later I replaced a problematic Node.js sever with Phoenix + Nerves running on a RPi Zero (ARM), flawless cross-compilation and deployment.
> Ports are not network transparent
I stand corrected, thank you for the explanation!
64 bit ints are a thing in JS for a while now
No, they aren't. You have to use BigInt, which will throw an error if you try to serialise it to JSON or combine it with ordinary numbers. If you happen to need to deserialise a 64-bit integer from JSON, which I sadly had to do, you need a custom parser to construct the BigInt from a raw string directly.
In case you didn't already know of it; CloudI is a cloud framework built with Erlang providing many of the features that you mention - https://cloudi.org/ See the FAQ for overview.
Incidentally, it makes Erlang-built systems robust. I used to run yaws-based web servers (and still do). One laughs at the logs, the feeble adversarial attempts to run this exploit or that. Nothing fits, nothing penetrates, nothing is even remotely relatable.
This is such a conflicting comment for me because I agree with so much but also have so many quibbles. That said I think that the other comments cover most things, I'll just comment on b: I don't think this is a problem that a language should solve or needs to solve, since there is a new flavor of the week of network protocols every few years. off the top of my head
- REST (mentioned, but what kind of REST? Rails style REST? Just plain http resource endpoints? - GraphQL (mentioned) - gRPC - SOAP - JSON-RPC - Thrift - CGI (ok not really in the same category as the above) - Some weird adhoc thing someone created at 3am for "efficiency"
I'm actually fine with most languages deferring to their respective communities, maybe building on core libs like https://www.erlang.org/docs/17/apps/inets/http_client to handle the transport layer.
As an aside, funnily enough you can get JVM <-> BEAM interop via https://www.erlang.org/doc/apps/jinterface/jinterface_users_... I don't necessarily recommend it but it's possible
> a) Ubiquity — everything understands HTTP. Erlang nodes only talk to Erlang (or a compatible runtime)
You're sort of confusing the purpose of Erlang distribution, so I would turn this on its head with the following questions:
Do the Python, Ruby, FORTRAN, Lua, etc, etc, runtimes provide built-in, standardized, network-transparent RPC, [0] or do you have to roll your own using some serialization library and network transport library that might not be The Thing that everyone else who decided to solve the problem uses? Do note that rolling your own thing that is the "obvious" thing to do is still rolling your own thing! Someone else might make a totally reasonable choice that makes their hand-rolled thing incompatible with yours!
I think this confusion influences the rest of your list. Erlang has several HTTP servers written for it [1], but it is not -itself- an HTTP server, nor does it use HTTP for its network-transparent RPC, service discovery, and monitoring protocol.
> The author's insight "No HTTP. No REST API", reframes the reality...
With respect, you're confused. The authors insight can be reworded as
> I can do RPC with no hassle. With just three symbols, I say 'Send this data over there.' and the runtime handles it entirely automatically, regardless of whether 'over there' is running in the same Erlang VM as the calling code or on a computer on the other side of the globe. With equivalent ease, I can say 'When someone sends me data, call this function with the transmitted data.' and -again- the runtime handles the messy details.
When I was first introduced to Erlang's distribution system, it also took me quite a while to detangle it from what I knew about the Web World. Reading through the 'Distribunomicon' chapter of Learn You Some Erlang [2] helped to knock those erroneous associations out of my head.
[0] ...let's not even talk about the service discovery and process/node monitoring features...
[1] Some of which totally do support middleware gunk.
[2] If you've not read LYSE, do note that it was first published in 2010 (and got an update in 2014 to cover the then-very-new Map datatype). Because the folks who work on Erlang tend to think that keeping old code working is a very virtuous thing, the information in it is still very useful and relevant. However, it's possible that improvements to Erlang have made some of the warnings contained within irrelevant in the intervening years.
There's probably not as much advantage to HTTP as you think.
The simplest RPC protocol is where you connect to a TCP socket, send a newline-terminated string per request, and get a similar response back. You don't need HTTP for that - you might still want to use JSON. What does HTTP give you in addition?
It's presumably still not something Erlang directly supports.
> What does HTTP give you in addition?
HTTP2 offers lots of nice features like stream multiplexing and socket re-use. I guess also encoding standards? Less of an issue in this day and age where everything can work with utf-8.
Presumably the fact that you can interoperate with other systems not part of BEAM is desirable too.
You also get this from newline-delimited-request-response. Multiplexing: send an ID with each request, and return the same ID in the response. Socket re-use: just keep reading until the client closes the socket. Encoding standards: you're the one designing it, so just say it's always UTF-8.
Lower layers of the protocol stack ossify faster in our minds than in reality.
It's a Galapagos island full of stuff that evolved independently.
Concurrency is important, but if you are not familiar with concurrency primitives like mutexes, condition variables, barriers, semaphores, etc. and skipped directly to the actor model, that's a bit like you care a lot about concurrency while not caring at the same time.
Functional programming is great, but your CPU has registers and the way it works is closer to the imperative paradigm.
If you like Erlang, I recommend reading Making Reliable Distributed Systems in the Presence of Software Errors by Joe Armstrong: https://erlang.org/download/armstrong_thesis_2003.pdf
His PhD thesis explains the thinking behind Erlang, especially how it handles failures, message passing, and concurrency. It was last updated in 2003, 22 years ago, time really flies!
Thank you for this, I'm unlikely to ever touch Erlang but looks like a fascinating read nonetheless.
It is a fantastic read. I find myself quoting section 5.3.1 on Well-Behaved Functions (WBFs) quite often, especially rule 2: "If the specification doesn’t say what to do raise an exception."
This really helps systems from muddling along into bizarre states, where things are going awry and nobody knows why.
I discovered Erlang from Bruce Tate's book[0] and it was such an obviously pragmatic and interesting language that I started participating in the Twitter Erlang community, discovered Basho was using it for Riak and looking for a tech evangelist in the Midwest, and thus began my all-time favorite job.
I'm still more of a infrastructure guy than a software developer, but working with such incredibly smart people was a delight. Basho was good at hiring people who could learn Erlang (and, perhaps unsurprisingly, was almost entirely remote).
[0]: https://pragprog.com/titles/btlang/seven-languages-in-seven-...
I can totally relate to this. Programming in Erlang felt so natural compared to the knots I was twisting myself into writing C++. I was churning out C++ code, but wasn't having fun. Suddenly Erlang made it fun and programming became addictive.
> Suddenly Erlang made it fun and programming became addictive.
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?
I'll try and answer this.
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.
It’s a subjective thing, but when a language clicks for you, work becomes pleasant. You get into (and maintain) a flow state. It feels as if you’re crafting something beautiful and elegant and simple. It’s the same reason people love painting, drawing, or sculpting with one specific medium over an other. They just like the aesthetic and process better.
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.
It's a bit like cutting wood. Cutting wood can feel terrible. You do it every day because you have to. Along the grain, in wet wood, with a handsaw.
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.
Until you hit a knot in the wood and get kickback, which leads to exciting times... j/k I get what you are trying to say.
Solving problems I guess. Have you used painful languages before? Imagine doing that, then discovering one that wasn't getting in your way all the time. It's easier to do things that are difficult in other languages. You can do so much on the BEAM, and you don't have to waste your time with thread pools or other nonsense.
For me, strictly immutable and side-effect free programming was a forcing function* to make me really decompose problems into isolated parts. Only when I could do that could I truly say I understood the problem (and its distinct sub-problems) in full.
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.
Every coder has parts of programming they love, and parts they hate. Additionally, they have a mental model of what is risky, and what isn't. Languages, by their design, will make some things easier and some things harder, and so when people "love a language," they typically mean "it maps to what gives me dopamine."
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.
Everyone talks about GenServers and OTP and all that, and that part is great, but what sells it for me is that it's a practical functional programming language. You get 80% of the FP part of Haskell but you don't have to mess with monads. It has tail call optimization which makes it easy to write functions in general, one "function" is made up of several that call each other, much easier to reason about and write. In general code I use what I call the "holy trinity", atoms, tuples and pattern matching.
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.
The ability to load recompiled code on running processes makes it possible to change live services on the fly. I worked for a low budget mobile operator in SE-Asia where there were no test platforms, and every change was on the live servers. Erlang made changes fast, as small changes were trivial, while large changes were broken down into small changes, implemented individually, and tested. Rolling back was fast, as I kept the original code before modifying. No convoluted build structures, and praying the rollout to production doesn't throw unexpected errors.
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.
when I program, I am visualizing the machine I am building in my head. some language semantics are cleaner than others, allowing the bits and pieces to flow together more naturally, composing in elegant ways that do not require pointless effort be spent manually wrangling footguns and minutia.
For me (and I've been writing Elixir for probably a decade now...) Joe Armstrong's post here encapsulates it pretty well: https://joearms.github.io/published/2013-11-21-My-favorite-e...
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.
Pattern matching and function guards sold me. I'll never look back, you can take patterns out of my COLD DEAD HANDS!
For me, it was Elixir pattern matching + GenServers + SupervisionTrees + Let It Fail mentality.
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.
You ever get a fresh pair of sharp scissors or a new very good knife? It feels like that.
It's kind of like this:
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.
> https://xkcd.com/224/
> “My God, it’s full of ‘cars’”
Classic
Ha, Ha! You and me both.
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.
> WHAT IS IT THAT YOU PEOPLE SEE!?
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.
I think the older programmer was hinting at gauss's formula with the summing 1 to 10 without using a loop? Recursion is also a loop in some sense.
Yep; the submitted article is just pedestrian with lots of posturing and pretty language to sell it. There is nothing of Programming/Mathematics/Erlang in it to deserve the upvotes.
Some of the comments here are far more informative.
[dead]
It's funny, because I find Erlang to be one of the least accessible languages I've ever tried (and my entry into this world of digital wonders was thru disassembling cracktros on a M68k, before internet, in a then-foreign language).
That said, the metaphors are so elegant, the key concepts so well chosen -- yes, the initial onramp may be a cognitive slog, but it's well worth it. It makes everything downstream so much easier.
It's funny how HN goes through these Erlang cycles. It's a long standing tradition, starting off with 'Erlang Day': https://news.ycombinator.com/front?day=2009-03-11
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.
> 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.
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.
I think what has changed mainly is that today we have tools, languages and entire ecosystems that exist only as means to support someone’s product line.
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.
When I was working with it (I was there, 4000 years ago) there was some talk about Swift for the server, but neither obj-C nor Swift ever really breached containment of the Apple ecosystem and -tooling. Which is a shame because at the time I enjoyed working in XCode. Who knew using a mouse swipe to go back in your code would be so natural? Not any other IDE developer, ever.
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.
That is why I don’t like those ecosystems. They’re all relying on magic (code generation and indexing) for everything instead of just providing a good notation.
If you’re creating that closed of an ecosystem, at least learn from history and create something like smalltalk.
Yeah, Swift was born because Apple had an ecosystem and they needed to fill it with a language. Erlang was born because Ericsson had a problem and they needed to solve it with a language.
It isn't entirely true though. The original Erlang guys made Erlang: The Movie https://www.youtube.com/watch?v=xrIjfIjssLE and the sadly missed Joe Armstong made plenty of blog posts https://joearms.github.io/ and public appearances and expressed a number of strong opinions (though genially) https://www.youtube.com/watch?v=lKXe3HUG2l4 . Robert Virding's public profile is not as high, but he's by no means in hiding either. https://www.youtube.com/watch?v=naTisAcey5Q — or indeed https://news.ycombinator.com/user?id=rvirding )
> 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.
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.
>somehow almost aggressive/corporate style even when there is no paid product.
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.
GNU Guix has a good blog, but I don't feel like they are very "marketing" focused.
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 think this is especially true of projects run by people with deep experience in the field and are in it “for the love of the game”, and don’t feel the need to stunt on everyone in hopes that they are taken seriously.
I think it's because we are wired that attention is it's own currency nowadays. And it's also true. Even if there's no paid product, you get strength in numbers. If you depend on an open source library, it's usually better for you if others depend on it too.
Certainly an element of that but there are also cases where the superior product "lost" to the inferior product because the inferior one was better marketed.
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.
> 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.
What were the major projects that changed that? Rails and Prototype.js feel like early examples where there was a major cult of personality around their creators, but I don't know if that was really unique to them or something that was just an extension of the webdev / css / web standards blogger culture of the era— A List Apart and all the people who would got to SXSW every year to chit chat with each other on panels and share the hottest new way to make a row of tabs without using a <table>.
I don't think it's cycles, more like newcomers rediscovering the future.
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.
> more like newcomers rediscovering the future.
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...
> but there is a sore lack of something closer to the metal within 80% the speed of a native language.
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.
> Why has no one put microkernels and Erlang into a blender? I know there's QNX, but it's still UNIX, not Erlang.
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.
You nerd sniped me a little and I'll admit I'm not 100% sure what a reduction is but I've understood it to be a measurement of work for scheduling purposes.
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.
Oh that's a really neat find. I'm not sure how 'instructions' map to 'reductions' in the sense that if you stop when a reduction is completed the system is in a fairly well defined state so you can switch context quickly, but when you stop in mid reduction you may have to save a lot more state. The neat thing about the BEAM is that it is effectively a perfect match for Erlang and any tricks like that will almost certainly come with some kind of price tag attached. An interrupt is super expensive compared to a BEAM context switch to another thread of execution, you don't see the kernel at all, it is the perfect balance between cooperative and preemptive multitasking. You can pretend it is the second but under the hood it is the first, the end result is lightning fast context switches.
But: great find, I wasn't aware of this at all and it is definitely an intriguing possibility.
> That would be an absolutely awesome project. Usually when you can think of it someone has already done it so a bit of googling
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?
I've got a hobby OS you may want to check out. Crazierl is a just enough kernel that can run the FreeBSD build of BEAM as a single OS process.
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/
Very neat! I have my own OS to work with that I know like my back pocket, so if I ever go this route I'll probably just carve that up.
Did you get your serial ports working? :)
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.
I got distracted (story of my life) by something very interesting that takes precedence but I'll return to it at some point, I've made up my mind about that.
That last link deserves its own HN post!
v86 has been posted a few times [1], I'm pretty sure that's how I became aware of it to use as a target platform. It's a nice project, and they've taken three PRs from me (so far) that make it easier for me to run my weird OS :) Lots of other contributions here and there from others which is great, because I thought I was going to have to build a virtio-net device in v86 or write a ne2k driver in crazierl, and other people built and debugged the virtio-net so I can just use it.
[1] https://hn.algolia.com/?dateRange=all&page=0&prefix=false&qu...
> Any reason you haven't posted it on Github/Codeberg?
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.
I hear you. These days all my projects are on private SSH repositories on my servers.
You may be one of today's lucky 10K.
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.
> want some immutable data structures and functions operating on them.
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.
I used to think that Clojure was going to be the first FP language that was going to go mainstream but it seems to have fizzled. What went wrong?
> What went wrong?
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
FP language is very hard on novice programmers. You can write thousands of lines of bad javascript/java/python code, but you won’t write ten in FP without the whole thing blowing up.
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.
yeah - without an easy ramp up for beginners most languages / frameworks die.
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.
In my experience it's still very possible to write many, many lines lines or utterly shit FP code, but I know what you're saying.
in some ways Elixir is a child of Clojure!
> 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
I work with elixir daily and I would concur. elixir's semantics line up nearly 1:1 with the clojure code I used to write a few years ago. Its basically if you replaced the lisp brackets with ruby like syntax. The end result is a language that is much easier to read and write on the daily with the disadvantage of making macros more difficult. I would argue that it should be difficult since you should avoid using it until absolutely necessary. Lisps on the other hand, practically beg you to use macros as the entire language is optimized for their use.
What about OCAML, does that fill your needs?
It’s perennially in my list of languages to check out. It felt harder when I looked into it 15 years ago. Now that functional programming is second-nature, it should be much easier.
Thanks for the reminder.
I really want to get into ocaml but the syntax is sooo ugly I feel like you need a great IDE set up to be able to be productive with it.
Might want to check out ReasonML.
> Why has no one put microkernels and Erlang into a blender?
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
Addendum to above:
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
Lets count the number of comercial sucesful released games that don't use mutability or object orientation....
I can't find any.
> Lets count the number of comercial sucesful released games that don't use mutability or object orientation.... > > I can't find any.
They use it, but the best tend to minimize mutability. And as for object orientation- merely 'having classes' is not the same thing.
Unreal, Unity, Godot, Source, virtually every game engine is object oriented.
> Lets count the number of comercial sucesful released games that don't use mutability or object orientation....
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.
How many games in Erlang have been released?
Oddly enough, you can write in a functional style in most any programming language. It's true!
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>
Leisure Suit Larry
Meh, I remember hearing that no one would release a commercially successful game in a managed language with a GC 15 years ago. Time flies.
Most things are said to be impossible until someone tries to do it anyway.
It's funny that you mention this, and it made me take some time to appreciate I've been working with Elixir full-time for almost 10 years now, and the entire experience has been so... stable.
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.
Being boring is the hallmark of technology that it is worth to invest a career into.
I've moved from Linux to OpenBSD for this reason.
It's all so boring it's wonderful.
I like OpenBSD, but I like Docker and Steam too much to daily-drive it.
FreeBSD can do OCI containers now!
For me it took a tremendous amount of work to somewhat understand the OTP stuff though. Its one of those languages where I can never be confident about my implementations, and thankfully it has features to check whether you have stale processes or whatever. A language I am humbled by whenever I use it.
Here's a trick to confidence in a BEAM system. If you get good at hot loading, you significantly reduce the cost of deployment, and you don't need as much pre-push confidence. You can do things like "I think this works, and if it crashes, I'll revert or fix forward right away" that just aren't a good fit for a more common deployment pattern where you build the software, then build a container, then start new instances, then move traffic, etc.
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.
Erlang's hot reload is a two-edged blade. (Yes yes, everything is a tradeoff but this is on another level.)
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.
You certainly can forget to push fixes to the source repo. But if you do that enough times, it's not hard to build tools to help you detect it. You can get enough information out of loaded modules to figure out if they match what's supposed to be there.
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_...
You can run https://www.erlang.org/doc/apps/kernel/code.html#modified_mo... in some process and make it send notifications to your monitoring when anything stays modified for too long.
That's part of it yeah. But, at least in my experience, that tells me you pushed code (to disk) and didn't load it. You could probably just notify at 4 am every day if erlang:modified_modules() /= []; assuming you don't typically do operations overnight. No big deal if you're doing emergency fixes at 4 am, you'll get an extra notification, but you're probably knee deep in notifications, what's one more per node?
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.
Thank you for this post and I'll add a note for people who are seeing this and are maybe discouraged about learning Erlang/OTP/Elixir.
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.
I love saying this but OTP is a really roughneck standard library. They just added shit to it as they needed it without apparently putting too much consideration into the organization, naming, or conventions.
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.
It also took me quite a bit of time to understand OTP. In fact, I had to have a project that actually required what OTP offered to really get it.
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.
1/3 of hn posts (maybe more) are "look at this thing we built!" or a combo of that with "plus all this VC money!" where op has basically re-invented something that has existed in Erlang since forever. I don't mind all these cool new things (it's why I visit). But personally, I prefer to cut to the chase and just use Erlang (well, in my case - Elixir).
I am just wondering when the next "We-Love-Haskell" cycle is going to start!
Feels like Rust has stolen a lot of Haskell thunder, in the sense you can write similar code and satisfy that functional programming itch in a much more popular language, while falling back on imperative programming if you really need it.
I haven't been following closely, but my impression is that the dependently-typed languages/theorem-provers have stolen some of Haskell's "most purely functional, most strongly typed" hype-thunder even though they're even further from being a mainstream implementation language for most projects.
Unfortunately, I have to disagree on the absolute notion, that you will never see someone being not nice in the Erlang world. OK, you technically said "behind Erlang", not "in Erlang ecosystem". But in the Erlang ecosystem and related languages I've had at least 2 encounters, that were not nice. One outright called lack of good documentation for some projects in the Erlang ecosystem bullshit in a public chatroom, and it killed all my motivation to continue exploring that part of the ecosystem, the other one probably without knowing gave low effort dismissive responses to questions about how to best do something, without even knowing the context in which my code appeared, mixing in their personal taste and treating me like a little child.
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.
I meant it the way I wrote it, so that makes this a strawman.
Every group of > 10 people will have at least one jerk in it.
> You'll never see the people behind Erlang be confrontational or evangelists
Can I interest you in my Rust project hosted in Jujutsu VCS?
Does anyone know if matrix.org has any Erlang implementations? It seems like the perfect fit to me.
https://www.uhoreg.ca/programming/matrix/polyjuice is closest
Nice write up. Thank you for taking time to evangelize it.
There’s no denying that Erlang is bad ass.
[dead]
> X equals X plus one? That’s not math. That’s a lie.
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.
I highly recommend How to Design Programs. I recall being repeatedly mind blown working through the book. It was great fun. The authors start by composing pure functions. IIRC you get quite far before you have to do any mutation. Take a look! https://htdp.org/2003-09-26/Book/curriculum-Z-H-5.html
Mutation should be an advanced topic in programming teaching. Even in procedural languages. It should be seen as an optimization technique you only use when analysis has shown that to be the only way to solve a difficult bottleneck. Using mutation as a basic tool in programming was a mistake.
Yes, see SICP (Structure and Interpretation of Computer Programs).
I saw it. That’s how I know it.
That's a funny thing to get hung up on, though I can understand it. I suppose that's the downside of some programming languages going with the worst assignment operator. If they didn't they could use = for (numerical) equality tests. If your wife has a strong mathematical background you could also show her some logic programming like Prolog or Mercury.
Also, various ways of assignment:
Pascal style:
Scheme: Forth:This blocked me from understanding computer programming for 3 years. I thought of 'variables' as immutable and the fact that they were not didn't really click at all. Once I mentally transitioned from 'it's a label attached to a value' to 'it's a named box which holds a value' life got a lot easier. I was 15 when it finally clicked (on a TI programmable calculator...).
I wonder if haskell would have made more sense to you at the time with everything being immutable.
The answer to that is surprisingly simple: no. Because Haskell didn't exist at the time. Neither did Erlang. But LISP did, but was just too expensive and memory hungry for mere mortals such as me. I had to learn all this on a shoestring budget working an 8 hour job and barely having left over enough money for food after rent, taxes and insurance. It took a long time before I was able to afford a floppy drive, much less a half decent computer, one that would be able to run LISP. And those language packs weren't cheap either and open source wasn't really a thing yet (but shareware actually was).
Or maybe Erlang.
That is fascinating.
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.
I learned to program in the 8 bit era, initially in BASIC on the TRS 80, then later assembly on the KIM-1 and then the 6809 based Dragon 32 (CoCo clone), which for the first time felt like a real computer. After that the BBC Micro and then the ARM, and eventually PCs.
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.
Boy can I confirm that happening to find the right book back then was absolute key. And they were so expensive! Never really bought one.
These days it's almost painful to come across great books that would have been so awesome to have in like 1984.
Another series that really helped me was Leventhal's assembly language series. The problems in the books were relatively simple but they gave you a hold on one CPU if you already knew something about another and they served as instruction set reference as well (which were ridiculously hard to get here in Europe, and I don't mean those little cards but the full data sheets).
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.
> She found it very frustrating [...] I wonder if I should try a [different language] with her
Or, perhaps, preprocess left-arrow to equal?
Customizing a language to taste. Especially for shallow syntactic issues. Like "no multi-line string literals allowed ... I'm sorry, that's just not ok". Or "ok kid, what nicknames would you like to give the keywords today"?
When programming for just fun, I'll sometimes go "this aspect of the language bugs me - I could of course just live with it... but here, I think it will be more fun not to". So for recent example, a few lines of recast.js ast tweaking, and I could use infix unicode mathematical symbols as javascript functions.
I wonder if one could teach programming by build-your-own language. Perhaps even of the video-scanned desktop variety. "Draw a circle around your toys to create a collection. Label it with a letter. Now you can sort/filter it by..."
I think it makes more sense in languages where you use the "let" keyword. Then it sounds like assignment, though you still have to get comfortable with the X being on the right side too.
It is not a lie it is just apparently familiar notation that actually has an entirely different meaning. It is not an equation, it is an assignment.
X := X + 1 is perhaps less confusing, even if meaning the same thing.
Although in Erlang it's closer to an equation with assignment being a side effect. `=` is the pattern matching operator.
That's exactly right. Looking at the Assignment Operator Wiki page, it's also clear where these notations come from.
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)
It’s not really such a different meaning, it’s just shorthand for something like:
Even if you treat it as “storing a value in a slot”, a comprehensive model of the operation needs to model the fact that the value of the slot changes over time. So another way to look at it would be: …except that “X” is shorthand for “the latest binding that uses this name.”I explain "X equals X plus one" as though each variable has an implicit version that increases whenever it's written to or as time goes on, but we can only access the lastest version.
For example
is reallyTell her there are transparent subscripts on each use of =.
x(0) = 1
x(1) = x(0) + 1
There's no way to reference the subscripts. They're implicit and the language manages it for you. Ask her to think of the Kronecker delta function and it's definition.
Cuis Smalltalk
Although you'd need to type and it might copy/paste asI think it's mental rigidity. In BASIC, the = symbol means both assignation as in
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.
Am I wrong to be skeptical that this was a thought author had at 8 years old? I don't doubt that 8-year-olds can learn programming and advanced math, but even the brightest 8-year-old would still be in the "sponge" stage. It's hard to believe they would have a sufficiently fixed idea about algebra to be troubled that X = X + 1 is "wrong".
Kids can be flexible but also rigid about the most random things. My two year old gets really annoyed if I use the wrong spoon to stir my tea, saying certain spoons are only for food or cereals. I don't know where she got that – it's definitely not a practice we ever enforced!
Yeah; the author is full of it - https://news.ycombinator.com/item?id=45896992
It's a common Tactic to pretend children are some sort of tabula rasa full of innocent wisdom that can be use to prove points.
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.
I've shared it here once recently, so might as well again.
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
Yes, people with mathematical background tends to fare better with declarative languages.
Pascal also had := for assignment, if I remember correctly. I disliked it, to be honest, = is pretty much universally accepted in the IT world to mean "assign to".
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
> I don't think this is something that keeps someone from programming.
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.
Thanks for sharing your experience. I guess in a case like this, even trying another language can work.
> “How can you sum the numbers from 1 to 10 without using a loop?”
Sum = n(n+1)/2
Yeah, I assume it's missing some context that the numeric solution was off the table.
When all you know is hammers, all you can think of are nails. Screws are impossible to use and understand. How are you supposed to hammer in a screw? All those ridges are in the way!
I remember being amazed when I learnt about summing arithmetric series.
Not even Erlang can beat Gauss
The hyperbole that Haskellers invoke around pure-functional vs impure is that you're never quite sure whether a function call you make will "fire the missiles".
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).> (mailbox = mailbox + message) is so simple!
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)`?
In Haskell with `head (sort list)` the entire list does not have to be sorted, depending on the sort implementation. Everything is lazy, so sort can sort the list just enough to return the smallest element.
Going beyond laziness, a compiler that can understand and exploit equations, could use `head (sort list) = smallest (list)` to make the program more efficient, going from O(n * log n) to O(n) complexity.
> 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.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?
Right. The author barely touched on the capabilities of Erlang/OTP. That section was pretty much a demo of the syntax and an advertisement of the fact that the language syntax and runtime system makes it trivial to have a distributed program that runs on separate machines.
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.
My point is, for an article about functional programming and Erlang, there wasn't any functional programming in Erlang.
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.
> there wasn't any functional programming ...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
> [ping and pong] only exist for their side effects.
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.
I think the question is whether sort should return a new, sorted array or whether it should sort the array in place. In functional languages it is the former, in imperative the latter.
It can be quite useful to have nondestructive sorting in imperative languages as well. Hence python introducing 'sorted' even though '.sort()' preceded it.
Unrelated to the topic, but I really like the feature `> cd ..` at the bottom of the article. It's simple and accessible even with a smartphone. I wonder if there is an easter egg inside.
Because the homepage has this "interactive terminal" which is funny and nice (kudos for readline shortcut support) but also partially "broken", i.e. the blog post(s) and other links you see with "ls" are not clickable, at least on Firefox.
I'd also love a `cd -` on the top left
Author here, I'm really surprised to see the story reached top 3. All of them is true and thanks to Erlang still surprises me.
My current stack is Elixir + Rustler on server and Rust + Wasm at frontend. Thanks for reading :)
Thank you so much for the write up! I've been avoiding processes for some reason while I've been learning Elixir and this post pushed me to play with it today. And I started in Erlang and then managed to translate it to Elixir successfully!
I ran into this issue and I'm not sure if it's just a me thing.
When sending the message from the Pong REPL, the following didn't work for me:
```erl {ping, 'ping@localhost'} ! {pong, self()}. ```
When I checked, the PID of `self()` is different to the PID of the registered `pong` process. I needed to change it to the following:
```erl {ping, 'ping@localhost'} ! {pong, whereis(pong)}. ```
This sends the correct PID to the Ping process and we have an infinite loop counting up.
can you give advice on frontend pertinent Rust crates?
R.I.P Joe Armstrong - he was an amazing man, engineer, and human overall. :(
I used Erlang for a couple of years. It's cool and elegant for sure, but it always felt like it took longer to write than JS or Py like I'd otherwise use. Recursion with matchers is sometimes natural, sometimes roundabout to what you want to do.
The multiprocess stuff is cool too, but a premature optimization for what I was doing. If I needed to scale, I don't know if that'd be the chosen approach.
I do a lot of Elixir programming, which is different and not at the same time :-) . What I find is that it takes me longer to write the initial code than I might in, say, JavaScript, but that the refactoring and ongoing maintenance of code is significantly faster. This is because I think the functional & actor paradigms force you to think harder upfront, but once those decisions are made are easier to manipulate. For me personally, I'd take that trade since I'm either refactoring or maintaining much more frequently... but I understand there are other considerations which could drive a different preference.
Also, I'm sure my personal experience isn't necessarily true for everyone... so weight this anecdote accordingly.
Thats interesting. In my case, elixir does take a longer time. Probably because i dont get used to or just use it not as frequent as other languange.
Would you mind telling me your elxir program about? Is it a webapps or something similar?
The one that I can share is at: https://github.com/MuseSystems/musebms
A few things to keep in mind. This is a "labor of love" personal project... sure a large one... but that's what it is. Another thing to consider is that I go off standard Elixir practices in a number of different ways and for reasons™; in this sense the project isn't representative of what a typical Elixir project looks like.
When I say that things take longer for me to initially code, I'm not including "self-inflicted" time dealing with novel issues which arise from taking a less common path... which does have its own time cost. In my previous comment I'm only really thinking of the typical aspects of Elixir development time.
Finally, I do need to emphasize: for me the cost is worth it. Once I get to maintaining that now-developed code or doing refactors, so long as I'm not throwing away everything and starting from scratch, the development time for me is rather less than I have with code of similar complexity developed in other languages. All personal experience... but there you have it.
One of the features I really like about the BEAM is that it solves the problem of organizing the topology of the applications I build. It's unlikely you're even close to doing this in other languages / frameworks.
The supervisor tree is one of the killer-app features of BEAM. I hit the benefits of this ALL the time. I notice how it keeps getting solved over and over again in unique ways that are half-baked. In some ways, this quote is what I'm hinting at, except, things don't even have to be concurrent. That's just a cherry-on-top.
"Virding's First Rule of Programming: Any sufficiently complicated concurrent program in another language contains an ad hoc informally-specified bug-ridden slow implementation of half of Erlang."
I'd also argue that "sufficiently complicated" bar isn't very high.
There are matured ways to do multi-service outside of BEAM. I get that it's extra work vs something built in, but this is only an issue at large scale, and you'd probably need to set that up anyway unless every service is Erlang/Elixir.
I want to get into Erlang, but I also know a lot of languages that are cool, effective, but rarely used in jobs.
That's the downside. If you look at the monthly "who is hiring" posts here on HN, you rarely see Erlang jobs. Elixir shows up a bit more often but it's still uncommon.
Yeah, I've really enjoyed the jobs where I got to use Erlang, but there haven't been enough of them. Even Elixir is kind of scarce.
Only had one, a contract where I got to choose the implementation. Job was to simulate a bunch of sensors on a network and Erlang was perfect for that.
My confusion here is it always seemed liked a simple mapping to take = to mean "make x equal to x+1" rather than "x is already equal to x+1".
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's why I like the
syntax better, or the syntax> even in the sum example, you see the statement "N is n-1"
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.
Oh my weary eyes, my bad
It's N1 is N - 1, not N is N-1
It is difficult to understand the full beauty (or horror, depending on your pov) of functional programming with such a simple example. But as you scale up in complexity, it can be someone’s full time job to make sure the model is accurately translated to the computer’s memory. With a pure language, the compiler is responsible for maintaining the mapping to memory, so you (mostly) just focus on the symbolic relationships of your model. This can be extremely freeing once you train yourself to think in functional terms. Of course, there is a performance cost, but this can be managed and in cases where people turn to functional programming reliability and correctness outweigh the cost.
Pure languages still use stack to store mutating state. In a single-threaded programs this is almost non-observable except for occasional stack overflow crashes. But with multiple threads and message passing one can emulate arbitrary state mutation just by using messages and stack.
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.
Calls a simple swap function with pointers inscrutable (the only hard to decipher character is the asterisk), and as a good counterexample provides this ?
I am not against functional programming, or using the tools you love, but at least make a valid argument about it ;)They didn't make such an argument. It's a biographical rumination about the authors personal development and feelings.
> ...at least make a valid argument about it ;)
Like other folks have said, show the equivalent C program and then we can have a discussion about implementation complexity and comprehensibility.
Do note that the equivalent C program permits IPC between two instances of the program that could be either running on the same machine or could be running on different machines connected by an IP network.
Edit: For general information, here's an Erlang implementation of increment and swap. Swap is made a bit complicated because Erlang doesn't support multiple returns... returning a tuple or list is the way that's handled, so I had it accept a two-element tuple for consistency with what it returns:
Port ping.erl and pong.erl to C and we'll compare them.
But I am not contrasting the two languages, I'm very aware of their respective strengths and weaknesses.
I am just disagreeing with the fact that C is hard to parse.
Decades long C programmer here. C is hard to parse. The thing is, you get used to it and then it feels like it is normal, but it really isn't. Not as bad as Perl, but pretty bad. And it is easy to construct one liners that are tricky to understand, I think C is the only language that has an obfuscation contest that keeps turning up gems year after year.
500 Internal Server Error nginx/1.24.0 (Ubuntu)
For what it's worth, I'm stuck on the very first x = x + 1 thing.
Not sure if you want to call it a screwup or bad grammar or whatnot, but it is perhaps the huge mistake that the "equals" sign was used for something that feels like, but emphatically DOES NOT mean, "is equal to."
It's "put this into that". It's an action verb. Should have perhaps insisted on x <- x + 1 or maybe better x + 1 -> x
> It's an action verb.
The difference is that it is an instruction. Conventional mathematical notation, while declarative by default, switches into instruction mode just the same with the "let" keyword. The usage of the "=" operator then becomes equivalent: e.g. let x = 1.
But as the aforementioned x = x + 1 comes from notations that are never declarative, where every statement is an instruction, the "let" keyword is unnecessary and redundant. You already know you are looking at an instruction.
> Should have perhaps insisted on x <- x + 1 or maybe better x + 1 -> x
While that would obviously work, it strays further from the conventional notation. Which doesn't matter to the universe in any way, but the topic of discussion here is about trying to stay true to conventional notation, so...
Is that the topic?
I do think what I'm trying to say here is: sorry, but your "conventional notation" sucks because "what is actually happening" is so very different from how the thing is overwhelmingly used for most people.
> Is that the topic?
Yes. "=" doesn't mean anything in the void of space. Conventional mathematical notation is what established "=" as meaning "equal to", as referenced by the original comment. But the same notation also uses it for assignment when in instruction mode, so imperative languages that use x = x + 1 syntax are quite consistent with it.
> but your "conventional notation" sucks
Maybe, but it's all anyone really knows nowadays. It's what you are going to learn in math class in school. It's what you are going to find used in mathematical papers. It is how you are going to express mathematical concepts to your friends and colleagues. Worse is better, I suppose, but it is what has set the standard. It is the de facto language of math. For whatever shortcomings it does have, virtually everyone on earth recognizes it, which is very powerful.
> so very different from how the thing is overwhelmingly used for most people.
I'm not sure how to grok this. let x = 1 is something anyone who has taken high school math will have encountered. Assignment is perfectly in line with the understandings of most people.
Do you mean that expression entirely using imperative constructs is unfamiliar to those who grew up with a primarily declarative view of math? That might be fair, but I'm not sure x = x + 1 is a specific stumbling block in that case. One has to understand imperative logic in its entirety to use these languages anyway, at which point nobody is going to think that x = x + 1 is intended to be declarative equality.
In one character
I prefer pascal's way: x := x + 1.
I believe you can blame Ken Thompson for this. In DMR's paper about early C history, he says:
> Other fiddles in the transition from BCPL to B were introduced as a matter of taste, and some remain controversial, for example the decision to use the single character = for assignment instead of :=.
I think Ken did most of the early design of B and DMR came along later to help with C. Ken has a famously terse style, so I can definitely see it being on brand to shave of a character from `:=`, which is what BCPL uses.
It's sort of a tricky little syntax problem. It makes perfect sense to use `=` for declarations:
At that point, you really are defining a thing, and `=` is the natural syntax for a definition. (It's what BCPL uses for defining most named things.)You also need a syntax for mutating assignment. You can define a separate operator for that (like `:=`). Then there is the question of equality. From math, `=` is the natural notation because the operator there is often overloaded as an equality predicate.
Now you're in a funny spot. Declaring a variable and assigning to it later are semantically very similar operations. Both ultimately calculate a value and store it in memory. But they have different syntax. Meanwhile, defining a variable and testing two expressions for equality use the same syntax but have utterly unrelated semantics.
Given that math notation has grown chaotically over hundreds of years, it's just really hard to build an elegant notation that is both familiar to people and consistent and coherent.
As you say there was a familiar declarative notation. Was there a familiar imperative notation?
Not familiar, perhaps understandable.Doubling down on my downvotes, then:
Look, I teach IT (to both novices and interested folks at a college) for a living; count this idea as one that, when I present it this simply, many a lightbulb goes on for those new to programming. Much like for zero-indexed arrays, I do very well with "look, this is stupid, but here's why it got that way, we'll just deal with it."
And while on occasion I take pride in nerd-dom, I feel like -- especially with the advent of AI for coding -- this is dinosaur stuff, where we would do better to go along with what's WAY more intuitive than to try to stick with mathematical or programming purity, etc.
That’s funny, because to me, it was always immediately obvious that once that line runs, then it must be true - once that line runs, x is now equal to whatever it was before, + 1. It’s the price opposite of the lie, it’s the literal definition of truth, in the sense that what is true is set by that line.
Programming is telling things to the computer, and in this case, you’re telling it what x is. Whatever you tell a computer is all the computer knows, whatever a computer does can only be what it’s told. If you never told it what x was, then x wouldn’t be anything… that’s the truth.
> once that line runs
This is the key point. Some people have a mental model that algebraic syntax is describing a set of immutable properties. You are defining things and giving them names, but not changing anything. There is no notion of time or sequence, because the universe is unchanging anyway. All you're doing is elucidating it.
Then there is a model where you are molding the state of a computer one imperative modification at a time. Sequence is essential because everything is a delta based on the state of the world before.
You have the latter model, which is indeed how the hardware behaves. But people with a mathematical mindset often have the former and find the latter very unintuitive.
It's hard for me to not suggest though that, essentially -- the math people are "right." Which is to say, "=" meant what it meant for 400 years, and then computers come along and redefine it to be an action verb and not an identity. And I think it's fair to consider that a mistake.
Fantastic article, my thanks to the author.
I must go dig up the Erlang book from my library. As I recall, it was a great read.
There is also Erlang the Movie 2 trying to sell Erlang (called Outlaw Techno Psyhchobitch in the video) with rock and hot chick: https://youtu.be/rRbY3TMUcgQ?t=262
And they were right, rebranding was all Erlang needed 13 years ago
That's absolutely terrible and hilarious at the same time.
Same guy as 'MongoDB is web scale'. I wish they'd done more videos; they're hilarious.
Will it ever run in WASM?
EDIT: there is Lumen, but not sure if it's stalled or still going.
Usage
It is not immediately clear what the intended usage pattern for Lumen and WebAssembly is.
Pros and Cons
Things we like:
Things we’re not big fans of:WebAssembly’s goal is to be a “portable execution layer” — an OS abstraction. If WASM becomes the standard runtime across browsers, servers, and edge networks, something still has to orchestrate thousands of concurrent tasks, message queues, supervisors, restarts, etc.
Erlang/OTP already solves that — beautifully.
So, the motivation is:
“What if BEAM’s concurrency runtime could itself be compiled to WebAssembly — and become the actor system of the WebAssembly world?”
That’s why projects like Firefly and Lumen are interesting: they’re exploring whether Erlang’s runtime model can become part of the WASM ecosystem — just like how Go and Rust shaped the serverless world.
> Will it ever run in WASM?
A ten-second search reveals [0]. (Have Kids These Days forgotten all about Emscripten?) However, given that web browser pages are often short-lived, I don't see what benefit bringing in all of Erlang and its VM gets you, other than the fact that you've pulled off the stunt.
[0] <https://www.antvaset.com/erlang-otp-wasm>
Great article & I love the command line interface on the home page.
this whole article stinks of AI prose, why should i read it
the author loves Erlang, but not to the point of writing their love themself
Great talk i love it and thanx for sharing.
Obligatory link to Erlang the Musical: https://www.youtube.com/watch?v=upiNIQOS4WE
Nice post, looking forward to more!
> “How can you sum the numbers from 1 to 10 without using a loop?” ... And there it was: recursion.
Wait, is there something I don’t actually understand about recursion? When a recursive function calls itself, is that not a loop?
It depends on how technical you want to get. It's certainly not a loop in the classic sense, like a for loop etc. Like there are no `continue` or `break` semantics and you can't return out of it (and yes yes of course you can still do all that stuff, just not with dedicated keywords). I call it a loop, though. Whenever I make a lil' server in Elixir (mostly always for teaching) I always call the function `loop`:
To put it another way: anything you can express with iteration, you can express with recursion (just as long as you don't run out of call stack).
Once you get it, it's obvious, but if you're not already familiar with the notion, the understanding can be pretty amazing.
> just as long as you don't run out of call stack
And with tail-call you don't even need to worry about that :)
Of course, that assumes that you write your functions so that they can be converted into iteration!
Did you know that some C/C++ compilers will do tail-call -er- optimization? I learned this quite a while ago, so I'd expect every major C/C++ compiler to do it by now, but I was pretty impressed at the time.
> Of course, that assumes that you write your functions so that they can be converted into iteration!
I was indeed assuming this, however due to my lack of compiler knowledge, not explicitly writing your functions--at least in the classic way--doesn't necessarily mean you Erlang functions won't be optimized. See section 2.3 [0]
Though again, I don't know shit about compilers and while I can imagine what callER optimized calls could look like, I would need some examples!
[0] https://www.erlang.org/docs/19/efficiency_guide/myths
> ...while I can imagine what callER optimized calls could look like...
What do you mean by "callER" optimized calls? That's a term I think I'm quite unfamiliar with.
> Though again, I don't know shit about compilers...
Oh, I also know fuckall about compilers. I'm just a bumbler who has muddled through a moderately-successful programming "career".
IME, on the topic of recursive functions, the big difference between Erlang and -say- a C++ program compiled with GCC is that the latter has a specific (but surely configurable somehow) call stack size limit which terminates the program if you exceed it. Whereas Erlang's limit seems to be "Well, how much RAM do you have, and how much space do I need to store away the arguments for each call?".
When I mentioned that some C/C++ compilers do tail-call optimization, what I intended to say was that they converted the recursive call into something more like iteration. I'm pretty sure that historically [0] the only way you could do this optimization is if the very last thing a function did was to call itself... doing anything after that call meant that the conversion to iteration was not possible. I have no idea if things have gotten quite a lot fancier in the years since I made this discovery and now.
If Erlang had a call stack whose size was limited by something other than the available RAM in the system, then whether or not your functions were written tail-recursive [1] would be quite a lot important, I think.
[0] And maybe it's still the case today? Refer back to my near-zero knowledge of compilers.
[1] I hope I don't forget that term again. It's much more succinct than my jumble about "tail-call optimization"
I have been trolling for a while now to find a great project to write in Erlang. I can't write any erlang and it's sufficiently foreign it's been fun to try to get my LLM to do it for me. This is because I cannot make any useful suggestions on _how_ to write it.
I have a toy replacement for syslog.
i supervised an LLM writing a discord-esq chat program that implemented a imaginary JMAP extension called 'JCHAT'.
i had the LLM write a Erlang based dimensional accounting system
So far in real life i don't have any uses, which is a shame because it's a great language and OTP comes so batteries included I can think of many use cases.
[dead]
The only thing holding me back from Erlang is the lack of type checking. Convince me?
https://www.erlang.org/doc/system/typespec.html
Yep. You feed files annotated with those to Dialyzer or similar for not-runtime typechecking. That plus the basic runtime checking provided by guard expressions [0] gives you a bunch of coverage... and I think some of the folks who like Elixir are working on their own typechecker thing? I don't really know, because I don't use Elixir.
[0] <https://www.erlang.org/doc/system/expressions.html#guard-exp...>
You might like https://gleam.run/, another BEAM language but with type-checking.
https://tour.gleam.run/basics/type-checking/
Gleam compiles to Erlang. So you can skip Erlang entirely but get all of the BEAM and ecosystem's benefits.
Gleam is very much worth checking out. It's syntax is very nice and natural feeling.
I have checked out Gleam and yes it does look delightful. Function captures are a really cool idea.