Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I cant help but read a lot of irony in this.

Erlang solved a problem really well over 20 years ago, its the sanest language by far that I have used when dealing with concurrent programming. (I havent tried go or dart yet) and I owe a lot of what I know to the very smart people building erlang.

However it has barely evolved in the last 10 years, will 2013 be the year of the structs? (I doubt it), every new release comes with some nice sounding benchmark about how much faster your programs will run in parallel and there is never a mention of whats actually important to programmers, a vibrant ecosystem and community, language improvements that doesnt make it feel like you are programming in the 80's. Better constructs for reusing and packaging code in a sane way.

Its fairly trivial in most languages to get the concurrency you need, I think erlang is solving the wrong problem in 2013.



> However it has barely evolved in the last 10 years, will 2013 be the year of the structs?

Eh... It is not perfect but considering everything else it brings to the table (fault tolerance, distribution, concurrency) I can easily overlook its warts.

There is always Elixir or some LISP like dialects running on the BEAM VM as an alternative.

I for one like its syntax. It makes sense to me somehow. I which maps were there and I think one day they will be. I would rather they'd work on concurrency, speed and distribution.


The syntax is a fairly minor point, and although its ridiculous there isnt first class dictionary support in 2013 there are parts of it I really like.

But Ericcson dont know how to manage an open source ecosystem, I dont think they particularly want to. It only started using an open source control a few years ago, still no open bug tracker, half the standard library is in terrible shape, there is no good support for 3rd party library integration.

A few years ago I wrote a UI to the documentation that most of the community seemed to massively prefer (http://erldocs.com). Every year I asked the OTP manager how to get it merged but instead they wrote their own (imo) sub par version while every release changing the documentation format which broke mine without warning.


> I havent tried go or dart yet

Well Go uses shared-memory concurrency and no other so...

Rust still looks more interesting there, though they still have to deliver the language (it's still heavily in flux)


Go allows you to share memory between goroutines (i.e. concurrent code).

It doesn't force you to do so.

In fact, the Go team explicitly tells you not to do that: "do not communicate by sharing memory; instead, share memory by communicating." (http://blog.golang.org/2010/07/share-memory-by-communicating...) i.e. they tell you to use the Erlang model.

But as everything, there are trade-offs. Even Go team uses shared memory protected by locks in some cases (see Go standard library) because it's faster or easier that way.

Every good idea (sharing memory between threads is dangerous and therefore should be avoided) taken to extreme becomes cargo cult programming.

Yes, it is dangerous, but at the same time there are plenty of successful projects that do it because there are programmers that can contain that complexity despite the somewhat popular view that this approach dooms you to failure.


An important side effect of not permitting sharing memory is that you can always recover sanely.

An Erlang process has its own heap, so when it blows up, its state just goes away, leaving your remaining program's state untouched. With Go, there is no way to recover sanely; even if your goroutines are designed to copy memory, Go itself has a single heap.

Now, this is a very odd design decision for a language that claims it's designed for reliability. Perhaps Go's authors thinks it's better just for the entire program to die if a single goroutine falls over; well, that's one way, but it's a crude one. Erlang's design is simply better.

I wonder if Go can ever adopt per-goroutine heaps, or whether it's too late at this stage. I was happy to see that Rust has chosen to follow Erlang's design by having per-task heaps, even if all the pointer mechanics (three times of pointers, ownership transfer, reference lifecycles and so forth) result in some fairly intrusive and gnarly syntax.


> Go allows you to share memory between goroutines (i.e. concurrent code).

Go will share memory, by default, and special attention must be taken preventing or avoiding it. It's not an allowance.

> In fact, the Go team explicitly tells you not to do that

And yet they have refused to implement a correct model, even though they have no problem imposing their view when it fits them (and having the interpreter get special status in breaking them, see generics).


> Go will share memory, by default, and special attention must be taken preventing or avoiding it.

Not really. If you use channels to communicate between goroutines, then the concurrency model is that of sequential processes, even if channels are implemented using shared memory under the hood.

That is, the default concurrency model militated by Go is not shared memory, but that of CSP. It's disingenuous to affix Go with the same kind of concurrency model used in C.

> And yet they have refused to implement a correct model

What is a correct model? Erlang's model isn't correct. It's just more safe.

> (and having the interpreter get special status in breaking them, see generics)

What's your point? Purity for purity's sake?


> Not really. If you use channels to communicate between goroutines, then the concurrency model is that of sequential processes

Except since Go has support for neither immutable structures not unique pointers, the objects passed through the channel can be mutable and keep being used by the sender. Go will not help you avoid this.

> That is, the default concurrency model militated by Go is not shared memory, but that of CSP. It's disingenuous to affix Go with the same kind of concurrency model used in C.

It's not, go passes mutable objects over its channel and all routines share memory, you get the exact same model by using queues in C.

> What's your point? Purity for purity's sake?

That the Go team has no issue breaking the rules they impose on others, so that point is irrelevant.


> since Go has support for neither immutable structures not unique pointers, the objects passed through the channel can be mutable and keep being used by the sender. Go will not help you avoid this.

Channels are pass-by-value. If you pass a struct, it's copied, and both sides can mutate their own copies as much as they want.

You can get still bugs if you make channels of pointers (or have pointers in your message structs etc).


> You can get still bugs if you make channels of pointers (or have pointers in your message structs etc).

Confirming that

> the objects passed through the channel can be mutable and keep being used by the sender. Go will not help you avoid this.


> Except since Go has support for neither immutable structures not unique pointers, the objects passed through the channel can be mutable and keep being used by the sender. Go will not help you avoid this.

I never claimed otherwise. But I do think you underestimate the utility of idioms.

> you get the exact same model by using queues in C.

No, you don't. C doesn't have lightweight threads, which means it can't support a useful CSP model of concurrency.

Just because Go allows shared memory doesn't mean its main concurrency model isn't CSP. Go doesn't force CSP on you, but that is nevertheless the primary concurrency model of the language.


> No, you don't. C doesn't have lightweight threads, which means it can't support a useful CSP model of concurrency.

Well there are a few libraries available for it.

And Windows C developers can make use of fibers.


>> (and having the interpreter get special status in breaking them, see generics)

>What's your point? Purity for purity's sake?

I assume the point is that the Go developers are saying "purity for thee, but not for me" (if you think generics are impure or unnecessary), or "generics for me, but not for thee", which is just annoying.


> which is just annoying

I got that. I'm asking, why?

The decision to use generics or not has nothing to do with purity. It's about trade offs. If a decent balance can be struck with special privileged functions built into the language, I don't see how that is intrinsically bad.


It's not a decision about whether or not to use generics, it's a decision about whether or not to make it possible to use generics.

The developers have decided that they should make it possible for themselves to use generics, but impossible for you to use generics. Do you really not see why that's annoying? That's not a matter of tradeoffs, because they've already made generics possible---for themselves. At best they might be thinking something like "only WE are capable of grokking when generics are appropriate; everyone else would abuse them", which is rather arrogant, no? Lots of people have been saying that go is "missing" generics. The official response seems to be "no it isn't, you don't really need them." The unoffical response (evidenced by the use of generics in the compiler) is apparently "you're right, go is missing generics, so we'll include them, but just for ourselves".


> It's not a decision about whether or not to use generics, it's a decision about whether or not to make it possible to use generics.

Fine. That's what I meant.

> Do you really not see why that's annoying?

I can if you believe in purity over all else.

> That's not a matter of tradeoffs, because they've already made generics possible---for themselves.

Sorry, but what? Are you really trying to claim that adding generics for the entire language has exactly the same trade offs as adding a few built in functions that are generic?

> At best they might be thinking something like "only WE are capable of grokking when generics are appropriate; everyone else would abuse them", which is rather arrogant, no?

I think you've significantly misunderstood the issue. I believe my initial characterization is right: you think this is about purity. It's not.

Please read Russ Cox's short blurb on the "Generic Dilemma." [1] No part of it has anything to do with "we know better than you."

> The official response seems to be "no it isn't, you don't really need them."

No. The official response is "we don't know of an implementation that we're happy with." It's not a philosophical stance. It's a practical stance.

[1] - http://research.swtch.com/generic


Honestly, I don't know what you even mean by purity.


The context of this discussion started when people were complaining about the language designers being able to build in special functions that are type parametric. The implication was: if the language designers can do it, why won't they allow me to do it?

i.e., purity for purity's sake. It ignores legitimate trade offs between allowing a few special functions and building an entire generics system into the language.


I still don't know what you mean by "purity". Saying "i.e., purity" doesn't help, since I already knew that you think purity has something to do with generics being definable by people other than the language designers.

"If the language designers find it useful to occasionally use type-parametric functions, why won't they recognize that other people might also find it useful, too, and for other functions?" doesn't strike me as a demand for purity in any sense. Consistency, maybe; recognition that the designers are giving themselves special treatment, sure. What's pure about the desired state of affairs, or impure about the present?

Honestly, sometimes it seems as if someone who wants to defend Go against a criticism immediately claims that the critic is just obsessed with purity. Why else would you criticize Go?


This was the original comment that I responded to:

> and having the interpreter get special status in breaking them, see generics

It's a whine that the interpreter gets "special status." It's implicit in the complaint that special status is somehow bad. It ignores any trade offs that go into the design decision.

Purity in this context means: the language gets no extra special power that users of the language can't tap into themselves.

> Honestly, sometimes it seems as if someone who wants to defend Go against a criticism immediately claims that the critic is just obsessed with purity.

And sometimes it seems as if someone criticizes Go just because they are obsessed with stuffing as many features as possible into a programming language. Shit happens.


>What's your point? Purity for purity's sake?

If go couldn't even implement its own interpreter in vanilla go, that's a sign that it's going to be a bad language.


Is it that they couldn't or haven't? It's obviously capable of compiling code, but with the state of compilers today (impressive backends, etc.) does it make sense to have a three-stage bootstrap (minimal C → mini-go → full-go → optimized-full-go) at this point?


It would be nice if Go provided for immutable variables.


for i:= range list { go func() { foo(i) } }

(This is a bug in the spec, but will not be fixed before Go 2 because Go 1.0 is frozen.)

Shared memory by default.


You've completely missed my point. I wasn't claiming that Go did anything to stop you from sharing memory. I was claiming that its concurrency model is CSP, not shared memory.

> (This is a bug in the spec, but will not be fixed before Go 2 because Go 1.0 is frozen.)

Could you kindly link me, please?


In fact, the Go team explicitly tells you not to do that: "do not communicate by sharing memory; instead, share memory by communicating."

Sadly, that doesn't mean what you think it means. It really means: don't organize your IPC around shared state. The juxtaposition in the second half is not directly related to the first half (except poetically), though it does do a good job of completing the their picture of CSP. Also note: it is explicitly talking about shared (sadly mutable) state.

You can always opt not to share memory, but there's no facility to prove or enforce it. It's not dire, with practice you can send values, or never mutate referenced objects. It is very natural for the most part. I've done it, but not in Go.

Finally, it's not doom. Even C can do threading after all, and somehow these things don't blow up too much. But there's value in eliminating the pitfall entirely. When people criticize Go for being imperfect, they're not saying it's not going to work, they're contrasting with a more effective solution or lamenting that some design decisions weakened the effort.


I guess the Go authors decided that for the sake of performance, you have to risk hurting your foot at least a little bit?


I was following you until your last sentence. I've never done concurrency in a FP language before, but I do know that writing it in Java makes it hard to get right.


What prevents you from implementing an actor/message passing system in Java?

Erlang's core concept of concurrency seems like something that'd be better suited as a library and app server than a whole language and runtime.

I've yet to hear of any Erlang-specific magic that cannot be implemented inside another language.


How would you get per-actor heaps that cannot be violated by other actors? That is critical to Erlang's ability to recover from processes dying. I spent a lot of time doing Java and can't think how you could (you could in the JVM if you had language constructs for it, but then we are back to a new language).

There's a reason Stackless Python's actors aren't just a library on top of Python.


TLABs are a partial step in that direction at the JVM level. You could also use object pools/factories keyed to the thread.

Those are the first two "ghetto" hack solutions I can think of that wouldn't require significant code changes on a going-forward basis.


But those hacks wouldn't provide the same guarantees that language-level changes provide. Sure, you can try not to impact other thread's heaps, but nothing is stopping me, which means a simple programming error has the potential to impact multiple threads. As a result, you can't just "reboot" that thread (a critical piece of what makes Erlang interesting), because you have no guarantees its errors didn't impact other threads. You also have no guarantees that the underlying libraries aren't mucking up all of your carefully crafted memory management.

It's like the kernel protecting memory so applications can't overwrite each other. Sure, applications could just write to their own memory, but nobody actually trusts that model[1]. Instead, they want something below that level enforcing good behavior.

1. Obviously, virtual memory adds a wrinkle to this that kind of forces kernel protection, but even if we had literally unlimited RAM, we would still implement kernel protections on memory.


Unfortunately, with any native code interaction, whether explicit FFI/NIF or implicit (RNG, clock, etc.), your threads can still mingle state.

In a single process, you're still limited by the memory virtualization capabilities of your underlying processor and MMU.


Erlang itself is, after all, implemented in C and ASM.

But what you can't (practically) implement yourself in other languages is all the professional care and maturity that have gone into the whole package over its long history. AFAICT, Erlang/OTP is much more than just a library.


Ultra-lightweight actors, a VM scheduler tuned for scheduling massive numbers of concurrent ops, etc. If you tried doing this in with a java library running java code, you couldn't get anywhere near their level of concurrency.


Obviously, you can do the same things in Java, as people have demonstrated with alternative languages that target the JVM.

It's the expressiveness at the language level that is really the "magic". For example, doing the equivalent of OO is not intuitive in Erlang, but completely possible (actually easy, but it looks...wrong) whereas it's supported by every Java tool. By the same token, pattern-matched message passing, lightweight green threads, and hot code deployment are primary concepts in Erlang.


You can at best implement a mimicry of Erlang's message passing in Java.

With sufficient effort, you can have the equivalent of no shared mutable data.

What you cannot have is completely separate heaps, so that if one thread crashes for whatever reason it doesn't take your application down.

Also, good luck trying to find a garbage collector that supports completely separate heaps that isn't a direct copy/near-identical implementation of the BEAM[1] VM's GC.

[1] The virtual machine that is the stock VM for Erlang. (In fact I don't know of any others but I have never looked.)


We've been doing it in Java since the nasty old days of RMI or "our own weird RPC over HTTP implementation". Lots of lightweight services with a supervisor to manage them and a directory to find them.

Now it's even easier with ESBs. Write a ten-line grails service to expose a bin-packing facility with Drools and then never touch it again.


You mean like the Scala and Clojure libraries? You can use those in Java code if you want.


Java's syntactic overhead stops most humans. "final" everywhere, big method names for every primitive message passing operation, etc


http://jlouisramblings.blogspot.com/2013/01/how-erlang-does-...

It's also preemptive too. I don't believe JVM have this nor any other languages.




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: