I understand that. That's not the alternative I want.
Go actually gets by with just one type of pointer syntax. Go is what a colleague calls "being stupid simple", which is really its strongest asset at this point. It eschews a lot of complexity by not trying to be clever.
The problem with Go's pointers is that no thought has gone into thinking about goroutines as boundaries of data ownership. It's simple to share mutable data between goroutines, and it's impossible to verify (manually or by any kind of analysis) that a program is not a shared-everything pile of spaghetti. This, plus mutable data, makes it impossible to safely implement things like Erlang-style remote task spawning.
What I would like in my ideal language is a single pointer type: A pointer into the task heap. However, pointers may only be shared between tasks by copying or by transfering ownership, except for immutable data, which can be shared freely with no overhead other than normal GC/refcounting. With some COW magic you could even make the copying really fast, too. Then all you need is a simple syntax for limited references that cannot be stored, only copied, so that you can actually have functions that work on data efficiently without leaking their pointers anywhere.
Maybe I am being naive -- I won't pretend to be a Rust expert. But this model works pretty well for Erlang. I find that while Rust is clever, it falls into the other extreme and becomes too clever; and being too clever is a kind of stupidity. For example, borrowed pointers sound neat on paper, but then you find you have to dick around with declaring pointer lifetimes. That's pretty insane. It results in a kind of syntax that represents everything I hate C++ for. I can understand that it took a while to decide on a syntax for declaring lifetimes. It looks totally weird and arbitrary. (Quotes are for strings, man!)
It's also possible that Rust just is not for me. :-)
I would also like it if we could just copy everything, but for implementing a competitive browser this gets really hard really fast. Erlang's actors are not really designed to squeeze every cycle out of the hardware. Incidentally, you can get this model with Rust's `flatpipes` module, which allows you to send data across Rust.
We experimented with COW designs in the early days. It seemed pretty hard to implement: hardware MMUs operate on page granularity, not on object granularity, so you need (thread-safe!) guards for all objects. That's pretty tough from a performance point of view. Perhaps it can be done, but it seemed less risky to just use uniqueness.
Indeed, the types of programs I write are very different from a web browser.
For what it's worth, I know I speak for at least a couple of other people when I say that we were, for a long time, hoping for a "better Go" -- we are mainly rubyists who want native compilation, a better/safer type system, better concurrent performance, more easily massively parallelizable -- and that Rust's development has been going in a slightly different direction than we were hoping. Perhaps it's time to re-evaluate C++11 or even Haskell.
Speaking personally for a moment here, I'm a Python/Javascript/PHP dev by day, and the reason that I'm interested in Rust is precisely because of all the new concepts that it's forced me to learn. I could never get into C because I enjoy having two feet, and I could never get into C++ because I know that there's no escape from that stygian pit. For me, Rust is precisely the right balance of low-level/safety/coherence, semicolons be damned.
Elixir is neat, but it's such a leaky abstraction; you still have to deal with a lot of Erlang stuff that Elixir doesn't cover.
Also, Erlang is slow. Really really slow. It's faster than Ruby, I think, but not much. And It sucks at a lot of things that should be fast in a functional language, such as file I/O.
Erlang itself is also neat, but the syntax is abhorrent (commas and periods instead of semicolons!), performance is awful, and the language has many warts (no Unicode, no structs, etc.).
I see where you're coming from. As for me, I am intimately familiar with everything that Rust provides, and it's frustrating because it's close to what I have been waiting for, but it makes a bunch of design choices I disagree with. Same with Go.
For now, Go is much closer to what I want than Rust, but performance is pretty awful for many things, so I'm not a happy clam.
If the goal is simulate n-body problems and solve one instance of sudoku it is not the right tool. If one needs a fault tolerant system to maintain 2M concurrent connections to clients then it is the best tool for the job.
Think about Erlang as a tank. A cool hipster comes to you says "LOL, my fixie bicycle is sooo much faster than your tank" and he's right because in some cases you just want to go to the nearest coffee shop to pick up an espresso. However a time comes when you need to go into battle and putting a turret and shields on the bike only works so far, then it is time for a serious tool for the job.
> performance is awful
I don't know I wouldn't call something that can handle millions of concurrent connections slow. It is slow if you all you need to do is handle a single connection at a time.
Fault tolerance is actually the main feature of Erlang everything else flows from it. You don't always need it but when you do need it there is nothing like it. Fault tolerance mean paying some performance penalty.
> syntax is abhorrent (commas and periods instead of semicolons!),
Don't those make more sense though? How does a semicolon ending a statement make more sense than a period. And if there are 3 things that are executed then comma seems quite natural as well. On the other hand few languages rival Erlang's pattern matching.
Erlang is great, but for me, its problems simply outweigh its benefits.
For example, not every app has a need for Erlang's massive parallelism. I can't write desktop or iOS apps in Erlang. Small utility scripts are cumbersome. There seems to be no kind of strong graphics or game support for it. For web apps the lackluster performance means is not really a step up from Ruby, which I currently use, so in that area there is no reason for me to switch.
In fact, the only area where Erlang is really perfectly suitable is in developing fault-tolerant, concurrent, parallel, distributed systems. And yet when I do need to write such a system, I look to Go instead.
> Don't those [commas and periods] make more sense though?
No, they really are a bad choice. First, any kind of statement terminator/separator should be unnecessary, as I have argued elsewhere. Secondly, distinguishing between two types of terminators is a bad idea because it makes editing harder. If I have
A,
B.
and I want to switch the order to:
B,
A.
then I can't just use my editor's nifty line-transposing mechanism. I have to edit each line and change the "," into a "." and vice versa. It's incredibly badly thought out.
Really, I like Erlang, but whenever its syntax is discussed, its defenders come out of the woodwork to make arguments without ever really wanting to listen. Syntax does matter. From speaking to lots of people, I know that Erlang's antique and cantankerous syntax is the number one problem preventing people from adopting it. As long as erlangers do nothing about their syntax, it will remain a weird little language that people admire but don't use.
Just look at Elixir -- people actually get excited about it. Because it has a nice syntax.
> few languages rival Erlang's pattern matching
Erlang's pattern matching is cool, but its reliance on ordered lists (which it calls tuples even though the fields are unnamed) makes it less cool again. I vastly prefer Haskell's pattern matching and overall approach to typing.
Go actually gets by with just one type of pointer syntax. Go is what a colleague calls "being stupid simple", which is really its strongest asset at this point. It eschews a lot of complexity by not trying to be clever.
The problem with Go's pointers is that no thought has gone into thinking about goroutines as boundaries of data ownership. It's simple to share mutable data between goroutines, and it's impossible to verify (manually or by any kind of analysis) that a program is not a shared-everything pile of spaghetti. This, plus mutable data, makes it impossible to safely implement things like Erlang-style remote task spawning.
What I would like in my ideal language is a single pointer type: A pointer into the task heap. However, pointers may only be shared between tasks by copying or by transfering ownership, except for immutable data, which can be shared freely with no overhead other than normal GC/refcounting. With some COW magic you could even make the copying really fast, too. Then all you need is a simple syntax for limited references that cannot be stored, only copied, so that you can actually have functions that work on data efficiently without leaking their pointers anywhere.
Maybe I am being naive -- I won't pretend to be a Rust expert. But this model works pretty well for Erlang. I find that while Rust is clever, it falls into the other extreme and becomes too clever; and being too clever is a kind of stupidity. For example, borrowed pointers sound neat on paper, but then you find you have to dick around with declaring pointer lifetimes. That's pretty insane. It results in a kind of syntax that represents everything I hate C++ for. I can understand that it took a while to decide on a syntax for declaring lifetimes. It looks totally weird and arbitrary. (Quotes are for strings, man!)
It's also possible that Rust just is not for me. :-)