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

Do you realize how silly this sounds? You are disregarding not only Erlang, but decades of research and development on concurrent programming best practices. Immutability has been promoted for parallel programming since the 1970s. For the past fifteen years, even languages like C++ have moved to promote this style of programming.

(Also, Erlang has a compile-time static type checker, if type safety is something you're after.)



Immutability is not a silver bullet.

One reason is RAM consumption: while Facebook has bought tremendous amount or RAM for caching content so those extra copies of objects don’t hurt, desktop or embedded apps have different resource constraints.

Another reason is performance. Memory allocation/deallocation is not free. RAM bandwidth for copying those objects isn’t free. Also mutable objects generally more cache-friendly, a modified object stays in the same RAM address i.e. stays cached.


"One reason is RAM consumption"

You can have immutability without data duplication; in an immutable-only environment, every process/thread/whatever that uses a specific object can safely reuse a pointer to that object and always know that the object represented by that pointer cannot change under their noses. It's only when you need to "modify" that object (read: create a new copy) where you start to see RAM consumption issues, and even that can be mitigated.

Linked lists are an example of where this works remarkably well; if you have a list (E→D→C→B→A) and want to append F to that list, you can do so trivially without needing to mutate or copy the original (since all you need for the new list is F and a pointer to E). Same if you wanted to replace E with F; just have F point to D instead.


It’s all good in theory, not in practice.

> only when you need to "modify" that object

Well, unless you’re modifying your data, almost all data structures are fine for multithreaded access.

> Linked lists are an example of where this works remarkably well

Linked lists are an example of what you don’t want to use if you care about performance even a tiny bit.

On modern hardware, random memory access is slow. A ballpark estimate is 100-200 CPU cycles just to read a single value (16 bytes for dual-channel DDR) from RAM. With a linked list you pay that cost with each iteration to the next list element. Very expensive.


Are you referring to Dialyzer? I've always wanted to learn Erlang but can't give up on types.


Yup, Dialyzer. Works with Elixir too (there's even a Dialyxir mix task) and gives me all the type checking I want -- which admittedly isn't much.


Personally, even when I do want something resembling type checking, I've found pattern matching / destructuring to be more than sufficient. With Elixir's structs, this is trivial:

    def some_function(SomeStruct{} = some_struct) do
      some_struct.foo + some_struct.bar
    end
Or better yet, if I only want certain fields:

    def some_function(SomeStruct{foo: foo, bar: bar}) do
      foo + bar
    end
This way, if the "type check" fails (that is, someone tries to use "some_function/1" on something that isn't a SomeStruct), there'll be a clear error to that effect.

Between this and protocols, I very rarely feel the need to reach for something like Dialyzer. YMMV, of course.


Pattern matching is fantastic. Also, there are guards:

  def handle_info(msg, state) when is_tuple(msg) do
    # ...
  end
I haven't written anything big with Elixir/Erlang yet, but I definitely feel more confident my Elixir code will work than Python, say. Not as much as Go, though.

As an aside, comparing:

  {:ok, port} = Port.open(...)
to something similar in Go:

  port, err := port.open(...)
  if err != nil {
    panic(err)
  }
That's very convenient. Also, you can bind multiple times to a single argument (sort of like what you showed):

  def handle_info([], state = {ppid, _port}) do
    Supervisor.stop(ppid)
    {:noreply, state}
  end
That's not a default argument, that's binding the second argument to state and the pid inside the state tuple.


I'm well aware of the literature.

The fact is that still today, we have millions and millions of lines of code that work in parallel and a crushing majority of it is operating in mutable languages (C++, Java, Javascript, C#, ...).

Pushing immutable languages as the silver bullet to solve concurrency problems is trying to solve a problem that nobody has.

We've moved on to higher abstraction grounds (fork join, work stealing, coroutines, etc...), Erlang is still stuck in the past.


I think immutability isn't really that important for concurrency in Erlang, since there's conceptually no shared state between Erlang processes -- if you use the mutable process dictionary, no other process could see it anyway[1]. In reality there is ets which is mutable with locks, and refcounted binaries which become immutable once there's more than one reference.

That Erlang forces you to build a shared nothing system is the real win for concurrency. Of course, you can do that with other languages too.

> We've moved on to higher abstraction grounds (fork join, work stealing, coroutines, etc...), Erlang is still stuck in the past.

Erlang can do fork/join, the schedulers can work steal, and erlang processes are approximately equal to coroutines -- I don't understand what you think is stuck in the past (other than the Prolog like syntax, but you get used to that).

Immutability is a big win for garbage collection and memory allocation however, since there are no circular references, mark and sweep isn't required and a simple (generational) copying collector can be used instead.

[1] actually you can look at other processes' dictionaries if you want, but that requires a lock on the destination process.


That still does not works. I have to maintain that code everyday. It does not work.




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

Search: