Hacker News new | ask | show | jobs
by jerf 5100 days ago
I've noticed that slowly, but surely, Haskell really is winning. C++ is now basically running as fast as it can to become Haskell. It's such an old language with so much baggage that "as fast as it can" isn't very fast at all, and it has no chance of ever reaching it, but the trendline is clear.

The question the programming community faces over the next, oh, ten years or so, is "Can we get the benefits of Haskell without the strict attention to the type system and without having to rigidly separate IO?" Or a bit more sarcastically/cynically, can we get the benefits without having to fundamentally change how we do business? My gut says no, but I'm open to being proved wrong. (Oh, and yeah that's not the only question, there's others like "What about OO? Can we keep it?", but I think that's really the core question; do we really have to rigidly control our side effects or can we keep our sloppy side-effect usage? Everything else is either incidental next to that, or flows from it.)

1 comments

The answer is no. Marking side effects is the one thing, probably more than any other that makes haskell awesome for building applications. For instance STM is awesome in Haskell because it is easy for the compiler to see any and all side effects.

The real thing that would be cool would be a strict haskell with optional laziness.

As I said, my gut agrees with you, but I think that given the relative newness of this idea, that the programming community at large should be given some time to make the case that the benefits can be obtained without so much of the cost. There's only a bare handful of languages that have even taken a serious swing at it, like Clojure and lately D. I want more evidence before I call it.

Probably the most promising approach that might salvage conventional programming approaches is a process-based model like Erlang or Go, where each process is internally mutable (mostly unlike Erlang, though it does have the process dictionary), but strictly segmented such that one process can not mutate another's state. This hybrid approach might be viable, and while it's not exactly business-as-usual, it's not as far a trip as full-on IO isolation. (Still, that affords just slamming everything in one process, at which point you don't win much. Will be interesting to watch Go's ecosystem develop and see if goroutines manage to become something deeply and pervasively used in all libraries or a thing occasionally used when the situation is desparate.)

> mostly unlike Erlang, though it does have the process dictionary

Yes and no, while Erlang structures are not mutable (aside from the process dictionary, and the process's message queue), each new iteration of the process loop mutates the process itself, as the process goes from one state to an other.

Erlang values are not mutable. You can't have a 4, lose the execution pointer to another process, and when it comes back you suddenly have a 5 in that variable. (Barring arbitrary C, of course.) That's the aspect of "immutable" that matters from a multithreading point of view. For most of what Erlang does, it would be fine to have mutable variables but immutable values, as if everything were as immutable as a Python string but you could freely reuse variable labels just as you can set a = "A", then a = "B" in Python. I think that's basically what Go does, though I haven't quite studied it enough to be sure.
> Erlang values are not mutable. You can't have a 4, lose the execution pointer to another process, and when it comes back you suddenly have a 5 in that variable. (Barring arbitrary C, of course.) That's the aspect of "immutable" that matters from a multithreading point of view.

Which is of no relevance to Erlang in the first place, since it does not expose threads to the developer.

> I think that's basically what Go does, though I haven't quite studied it enough to be sure.

Go has mutable structures and mutable bindings.

"Which is of no relevance to Erlang in the first place, since it does not expose threads to the developer."

It may not expose them to the developer in the sense that they are available for the developer to directly manipulate, but it is certainly exposed that multiple cores may be running Erlang simultaneously. To the extent that it isn't relevant to Erlang, it is because Erlang has made it not relevant to Erlang by a conscious choice of the developers, not some sort of accident.

"Go has mutable structures and mutable bindings."

Yes, I said that, but can a structure owned by one goroutine be directly modified by another such that a single goroutine can observe that a reference has changed values which the goroutine in question has not changed? Do structures even belong to goroutines? One of the things that turned me off when I looked at it a while ago was that the docs were giving me a hard time answering this question, but I consider it a rather fundamental one. (But this was a while ago, much closer to its beginning.)

For all my love of Clojure, having used its STM implementation I can wholeheartedly agree with this.

Having side effects in a transaction occur more than once because you didn't pay attention is a real pain to debug, mostly because the transaction won't be retried until the program is exposed to heavy load leading to serious memory contention.

At that point, debugging concurrent designs turns into a nightmare, not being any better than having to deal with deadlocks etc, the very thing STM is supposed to magically make go away. In some cases I even had to resort to locks because it was easier to handle IO that way.

Now, Haskell's type system would just not allow having any sort of IO side effects inside a STM transaction. Yes, having to explicitly handle IO may sound as a lot of work, but in my experience it makes everything easier.