News for hackers. Stuff that matters.
Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by coder543 on Friday September 11, 2020 @03:28PM (#24446424)
> * Erlang is a much simpler language

Go is widely considered to be a very simple language, often too simple, so I find this claim interesting.

> * Despite lacking a static type system, Erlang's functional bend, immutability, SSA and pattern matching makes for a rather safe language

This is fine stuff, but I would rather have a type system, so this is just making up for a deficiency with some other stuff, so it's really just a sidegrade, not so much an upgrade or a downgrade.

> * Erlang has always been fully preempted, it didn't have to wait until 2020 for that to happen

With extremely rare exceptions, Go has always been fully preempted from a developer experience point of view. The fact that the underlying implementation was using function calls as yield points simply didn't matter. The fact that it is fully preempted now means it's kind of weird to still hold it against Go that they are doing the same thing as BEAM now.

It is done, this is no longer a bullet point.

> * Erlang also builds distribution in, though it's "trusted distribution" so probably of fairly limited use at large

Go has always "built distribution in" by offering to build single static binaries. If anything, BEAM languages are harder to deploy.

> * Erlang uses shared-nothing concurrency, greatly limiting side-effects across processes, which again helps with reliability: there is no action at a distance in sequential erlang, no third-party can come in and mess with your state

I agree that immutability is an advantage. In general, third parties can't come in and unexpectedly mess with your state in Go either, as long as you're either maintaining your state local to a function or keeping your global variables private to each package.

Keep in mind that Go's package imports must form a tree with no loops. Third party packages literally cannot see your code or any of the exported global variables, since you are importing them, and not the other way around.

Go does not offer any way for packages to reflect over the whole program's state in memory, so there's no way for third party packages to go hunting around in memory and playing with your values, which would be stupid, unless they use "unsafe" to start poking at random memory addresses, and I'm certain you can do something equally dumb in Erlang with a sufficient amount of effort. It's just as crazy to think of doing something useful that way in Go as it is in Erlang.

> * Erlang builds reliability into the language and runtime through concepts of liking, supervision, …

This is literally the only compelling talking point I've seen for Erlang/BEAM vs Go.

However, I've written large Go applications with distinct services running within the same binary, and it is possible to make extremely reliable systems. Go's context package allows you to build trees of processes that you want to be able to cancel, including cancelling specific sub-trees without affecting adjacent trees or parent trees.

The single biggest footgun with building reliable systems in Go is that it prefers to crash early. So, every Goroutine that you launch, you must make sure that the first thing it does is catch any panics to avoid taking down the entire program, including unrelated Goroutines. When correctly written, a human will be immediately alerted to the bug that caused this panic (which shouldn't ever happen anyways), and the program will continue operating normally in the mean time until a human gets a chance to push out an update to fix that bug.

In practice, enforcing just one rule (Goroutines must catch panics) in code reviews is not that hard, and it isn't common in my experience for most code to arbitrarily launch Goroutines -- Goroutines should be managed.

In return, Go offers much better performance than BEAM, as well as most of the practical benefits of BEAM that I've seen discussed online.

I think Rust is a better language than either Go or any BEAM language, but the absence of a preemptive concurrency model like Go and Erlang offer just makes it hard for me to enjoy the prospect of using Rust for general purpose network services.

Once Go adds generics in the next year-ish, that should solve my biggest real annoyances with the language. I really look forward to being able to package up a number of the concurrent patterns I write into reusable packages, which will largely eliminate once and for all the need for me manually spin up a new Goroutine in the code that I'm writing and potentially forget to catch any panics.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by Jtsummers on Friday September 11, 2020 @03:42PM (#24446538)
> > * Erlang also builds distribution in, though it's "trusted distribution" so probably of fairly limited use at large

> Go has always "built distribution in" by offering to build single static binaries. If anything, BEAM languages are harder to deploy.

I suspect GP meant distribution in the distributed computing sense, not the deployment sense. What, baked into Go not via a library, handles multiple Go programs communicating within the same physical node or across multiple physical nodes? Erlang gives you the same communication method whether distributed or within a single node, Go does not (that is, Go channels are internal to a specific program, they cannot be referenced from different programs or computers).

If they did mean it in the deployment sense, I'd still argue it's a point in favor of Erlang. Two nodes communicating can execute code on the other.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by coder543 on Friday September 11, 2020 @04:01PM (#24446739)
Yes, I definitely misunderstood the point being made there.

> What, baked into Go not via a library, handles multiple Go programs communicating within the same physical node or across multiple physical nodes?

Go does have an RPC package built into the standard library, for what it's worth, but I know it's not the same thing as the runtime transparently stitching distributed machines together under the hood.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by masklinn on Friday September 11, 2020 @03:48PM (#24446605)
> This is fine stuff, but I would rather have a type system, so this is just making up for a deficiency with some other stuff, so it's really just a sidegrade, not so much an upgrade or a downgrade.

Sure, I'm just saying the lack of a type system in erlang is not quite as bad as, say, in javascript.

> Go has always "built distribution in" by offering to build single static binaries. If anything, BEAM languages are harder to deploy.

Distribution in the sense of distributed computing, not in the sense of giving somebody else binaries to run. That is not something erlang is good at. But you can rather easily join nodes together and not just have them communicate but have them leverage one another e.g. spawn processes on an other node in the cluster.

> I agree that immutability is an advantage.

Immutability and isolation are different things, though Erlang has both.

> In general, third parties can't come in and unexpectedly mess with your state in Go either, as long as you're either maintaining your state local to a function or keeping your global variables private to each package.

Go is a shared-memory concurrency language, with no restrictions on the sharing.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by coder543 on Friday September 11, 2020 @03:54PM (#24446673)
> Distribution in the sense of distributed computing, not in the sense of giving somebody else binaries to run. That is not something erlang is good at. But you can rather easily join nodes together and not just have them communicate but have them leverage one another e.g. spawn processes on an other node in the cluster.

Ah, I misunderstood, sorry. Personally, I see limited benefits to making things fully distributed at the runtime level. I'm sure it's nice sometimes.

> Go is a shared-memory concurrency language, with no restrictions on the sharing.

Sharing is the critical word there. You don't have to give access to that shared memory to anything, but the point I was making is that nothing can take shared access to your stuff without you handing it to them. It's not the wild west of interpreted languages where any piece of code can access all of your code and variables and edit them just for fun without you realizing.

Go also supports channels. How you design your system is up to you, and doesn't BEAM also support shared memory concurrency? A little thing called ETS? I would guess that there are other escape hatches as well, but I'm not familiar enough with Erlang or the BEAM to say for sure.

If you want to make a poorly designed system, I'm sure that Erlang won't stop you, but it will obviously try to guide you into certain accepted patterns by making other things harder.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by toast0 on Friday September 11, 2020 @05:11PM (#24447359)
> Personally, I see limited benefits to making things fully distributed at the runtime level. I'm sure it's nice sometimes.

I think it's nice because when you write code as processes that receive requests and send responses, they can handle requests from the same node or different nodes with no change in code, you only change where you start that process (and make sure you register the process somewhere either as a named registered process or pg2/pg or whatever). Certainly there can be some scaling difficulties (see Rick Reed's talk linked elsewhere), but it generally works. And, you can move the processes while the system is running to address changing needs. Building on top of asynchronous messaging makes it easy to do things like normally wait for a reply after sending for a synchronous feel, but occassionally doing multiple sends and then waiting for parallelism, or just sending without waiting for a reply when that's appropriate.

> BEAM also support shared memory concurrency? A little thing called ETS? I would guess that there are other escape hatches as well, but I'm not familiar enough with Erlang or the BEAM to say for sure.

Ets is built with shared memory, yes, but interaction with your code is as if you were messaging a table process; the data you get back from an ets read won't change while you have it, but it might not be what you wrote, because another process may have written something else; same as if another process messaged a storage process in between your write and read. ETS is less of an escape hatch and more of a performance optimisation -- acknowledging shared memory concurency is helpful for data storage and retrieval, but it's tricky to get right and should be written once, so BEAM doesn't give the tools to do it and it needs to be done in C.

You can also do fun things with erlang:process_info or tracing to snoop on some data from other processes (but I don't think modify?). And, BEAM allows native code (NIFs) which is the ultimate escape hatch --- you can do lots of fun things there, of course.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by coder543 on Friday September 11, 2020 @05:25PM (#24447486)
> Ets is built with shared memory, yes, but interaction with your code is as if you were messaging a table process

To me, this is a distinction without difference.

Shared, mutable memory access without some form of synchronization is a bug and is always invalid.

You either hold a lock the entire time you're using a shared value, or you use a lock to take a copy of the value and then go about your business. With the singular exception of atomic values, the value should never change while you have it without you taking an explicit action to update it, and atomics are rarely used outside of either metric tracking or very specialized code. Besides atomics, everything you described about ETS is the same as any shared memory system.

Re:Scaling Erlang Developer Experience at WhatsApp [pdf] by toast0 on Friday September 11, 2020 @05:43PM (#24447648)
> Shared, mutable memory access without some form of synchronization is a bug and is always invalid.

That doesn't prevent it from being really easy to write, and sometimes very hard to notice in a lot of languages. In BEAM languages, you can, of course, set up similar bugs through the use of other processes (or ETS), but you have to work harder to do it wrong than to do it right.