News for hackers. Stuff that matters.
Re:Reflecting on Haskell in 2017 by devit on Monday December 04, 2017 @07:23AM (#15843318)
My impression of Haskell is that the main issue is that laziness is simply wrong, since it causes space leaks that are hard to reason about, and seems generally less efficient than eagerness; furthermore, it seems rarely beneficial, so it seems more appropriate to explicitly request laziness rather than the opposite.

The next problem is that complete immutability is also wrong, because controlled mutable aliasing with linear types (like the system available in Rust) is what you really want since it's more general, efficient and expressive (allows mutable data structures), and complete immutability is just a special case of controlled mutable aliasing.

The third problem of Haskell is the weird syntax, that doesn't follow the common C/Java/JS or Python syntaxes for no good reason, making it hard to read and learn the language.

And if one were to change these things in Haskell, the result would essentially be Rust (or more precisely, a future version of Rust once all the missing abstractions like HKT, specialization, etc. are added), so I think that's what one should use instead.

Re:Reflecting on Haskell in 2017 by lucozade on Monday December 04, 2017 @08:36AM (#15843639)
> laziness is simply wrong

No it's not. It's not a particularly sensible default (which you allude to) but it's not wrong. Modelling delayed resolution is very handy. For example, you'd not get far in Rust without Result<>, Option<> or Future<> which are all make use of delayed resolution for their value. And the more we move to parallel models, the more important it will be.

> because controlled mutable aliasing with linear types ... is what you really want

Again, no. It's less restrictive than pure immutability, and Rust has shown that statically managing some mutability is viable, which is great, but it's not what you really want.

What you really want is a static checker that only prevents shared, simultaneous mutability but admits everything else. The Rust borrow checker is an important step in that direction but it's not it.

> The third problem of Haskell is the weird syntax

I'm not one of life's Lisp apologists. Syntax is important, though not as important as semantics. But just because you find it difficult to read doesn't make it objectively difficult.

And if you're implying that Rust is easy to read, for a beginner, because it uses curly braces, I'm not entirely sure what to say.

I think Rust is an excellent language. I hope and believe it will be an influential language as it's a fantastic combination of innovation and pragmatism.

I'm also enormously grateful that a lot of the decisions are made in the open, I've learnt a lot and continue so to do. But it's a good language because the designers clearly have an appreciation of what has gone before. That's a good way to be.

Re:Reflecting on Haskell in 2017 by devit on Monday December 04, 2017 @09:18AM (#15843948)
Obviously a language needs to be capable of expressing laziness (which requires closures and some mutability), but it being the default is the issue in Haskell, since basically everything returns a closure computing the value rather than the value itself, which is really weird and unexpected, and causes unintuitive (at best) memory usage.

As for implicit parallelization with no annotations at all, the main Haskell implementations don't seem to do it, so I guess it just doesn't work (the reasons probably being that there are penalties due to caches being CPU local and the cost of synchronizing threads that make automatic micro-parallelization much worse that explicit coarse-grained parallelization, unless the micro-parallelization is done in hardware by the CPU out-of-order logic).

And obviously a type system with controlled mutability like Rust's allows to express explicit parallelization just fine (in fact, writing a safe explicitly parallelized web browser in Servo was one of the main goals of it).

> What you really want is a static checker that only prevents shared, simultaneous mutability but admits everything else. The Rust borrow checker is an important step in that direction but it's not it.

Is there any example of a static checker that allows more expressive programs than Rust's while preserving the same guarantees? (and without requiring extra annotations like programmer-supplied proofs or using best-effort automatic theorem proving). Is that even possible?

Re:Reflecting on Haskell in 2017 by lmm on Monday December 04, 2017 @11:01AM (#15844827)
> And if you're implying that Rust is easy to read, for a beginner, because it uses curly braces, I'm not entirely sure what to say.

Rust is easier to read because the visually natural distinction between words and punctuation has the same kind of meaning in Rust that it does in prose. Reading Haskell feels like reading a paragraph without any punctuation; there's no structure to grab onto. A lot of the time you can't tell whether something is a type parameter or a value.

Also $ is utterly alien compared to any other remotely mainstream language, . means something very different from what it usually does and what it usually means is rarely used ("subject verb object" is very rare in Haskell code), the :: type syntax is obscure, the mandatory split/duplication between function declaration and definition feels weird...

Re:Reflecting on Haskell in 2017 by runeks on Monday December 04, 2017 @12:39PM (#15845701)
> A lot of the time you can't tell whether something is a type parameter or a value.

> […] the mandatory split/duplication between function declaration and definition feels weird...

I believe you just answered your own critique: you can always tell whether something is a type parameter or a value, because type and implementation are kept separate.

Re:Reflecting on Haskell in 2017 by lmm on Monday December 04, 2017 @01:41PM (#15846306)
They're in separate clauses but they look the same on their own. You have to look out to a few lines of context to see which is which. At a glance it's not even obvious which lines are function declarations and which are implementations.
Re:Reflecting on Haskell in 2017 by lucozade on Monday December 04, 2017 @11:21AM (#15844997)
> natural distinction between words and punctuation has the same kind of meaning in Rust that it does in prose

I'll just leave <'a> here then. Hard to resist. Sorry.

Re:Reflecting on Haskell in 2017 by mookid11 on Monday December 04, 2017 @03:00PM (#15846886)
> For example, you'd not get far in Rust without Result<>, Option<> or Future<> which are all make use of delayed resolution for their value.

Certainly not for Result<> or Option<>.

Re:Reflecting on Haskell in 2017 by msla on Monday December 04, 2017 @02:27PM (#15846665)
I like Lisp precisely because syntax is too important to be left to programming language designers.

No other language family has true macros. Lisp does, because it takes syntax seriously.

Re:Reflecting on Haskell in 2017 by fasquoika on Monday December 04, 2017 @03:41PM (#15847280)
> No other language family has true macros

Forth?

Re:Reflecting on Haskell in 2017 by marcosdumay on Monday December 04, 2017 @12:14PM (#15845466)
> It's not a particularly sensible default

I'm not convinced there is any sensible default for that. By now I think lazyness is something that will only be solved by interactive optimization.

Re:Reflecting on Haskell in 2017 by d3ckard on Monday December 04, 2017 @07:28AM (#15843343)
Totally disagree and as of now I only dabbled in Haskell (though it's first on my to-do list).

1. Immutability works when everything is immutable. Otherwise you're never sure and you lose benefits. 2. Syntax is a non issue. Honestly, I stopped caring a long time ago about how language looks. You get used to it in the first month. 3. Laziness is disputable. While it can be a optimization problem, it also allows for more generic description of ideas.

Re:Reflecting on Haskell in 2017 by acdha on Monday December 04, 2017 @10:23AM (#15844466)
> 1. Immutability works when everything is immutable. Otherwise you're never sure and you lose benefits.

That seems like a poor way to explain the concept since the entire point of programming is to mutate state and that's how the hardware actually works, too. I prefer to discuss it as tackling that uncertainty by allowing the language to indicate more nuanced contracts and enforce them, where immutability-by-default is a reasonable design which still gives you the option of using a mutable structure where necessary.

> 2. Syntax is a non issue. Honestly, I stopped caring a long time ago about how language looks. You get used to it in the first month.

I think this is a big mistake: if you want a language to be successful, that first experience is pretty important if you want big open source community with many contributors. In the case of Haskell, this is especially important since there are a number of other hard concepts which people need to learn along with the syntax and the nature of the language means that other programming experience is less helpful.

Re:Reflecting on Haskell in 2017 by AnimalMuppet on Monday December 04, 2017 @01:33PM (#15846251)
> That seems like a poor way to explain the concept since the entire point of programming is to mutate state and that's how the hardware actually works, too.

The hardware actually works using gotos, too (well, jumps in assembly language). That does not make it either ergonomic or safe to write code that way.

The entire point of programming is to compute results. In the hardware that results in mutations; in the programming model it doesn't necessarily have to.

Re:Reflecting on Haskell in 2017 by acdha on Monday December 04, 2017 @02:36PM (#15846739)
My point was simply that saying “immutability works when everything is immutable” often sounds confusing and/or wrong because people know they need to mutate things. Saying that you're using a language with more advanced concepts to ensure that things only change in the desired manner avoids that initial point of confusion.
Re:Reflecting on Haskell in 2017 by devit on Monday December 04, 2017 @07:59AM (#15843458)
In Rust, if you have an & or Rc/Arc, you know that it's immutable, except for parts contained in Cell/RefCell/Atomic/Mutex.

If you have an &mut or owned mut variable, then you know that you can only mutate it through that name.

If you have a non-mut owned variable, then you know that only the Cell/RefCell/Atomic/Mutex parts can be mutated, and only through that name.

If a Rust program doesn't contain the words "mut", "unsafe", "Cell", "RefCell", "Mutex" or "Atomic[...]", then everything in it is immutable.

That gives the same guarantees as Haskell's immutability where desired, but it allows to actually make use of CPU capabilities like writing to memory, and express basic operations like assigning a value to a position in an array.

Or in other words, what you really want is a system that lets you mutate something as long as you are the only one with a reference to it, and then make it immutable and give it around, while also allowing you to "get it back" exclusively either with static checks (via lifetimes) or dynamic reference count checks.

Haskell's pure immutability system, while safe unlike Java's, is just a very restricted version of such a system, that is not powerful enough to write safe programs that are as efficient as C programs (since you can't modify memory), or express imperative code even when they are safe.

Re:Reflecting on Haskell in 2017 by kccqzy on Monday December 04, 2017 @11:47AM (#15845242)
You make it sound like Haskell doesn’t have the capability to do things like “writing to memory, and express basic operations like assigning a value to a position in an array.” That’s utterly false. It’s not impossible or unusual in performance critical code to deal with mutable bytes strings and mutable arrays extensively. If you want you can just write to arbitrary locations in memory and cause segfaults in Haskell.

Mutable arrays (this is used more often than you thought): http://hackage.haskell.org/package/vector-0.12.0.1/docs/Data...

Raw pointers (types, casts, and pointer arithmetic): http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreig...

Raw pointers (dereferencing them, reading and writing memory): http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreig...

Manual memory management (malloc/free style): http://hackage.haskell.org/package/base-4.10.0.0/docs/Foreig...

Re:Reflecting on Haskell in 2017 by Tarean on Monday December 04, 2017 @08:54AM (#15843777)
> what you really want is a system that lets you mutate something as long as you are the only one with a reference to it

You mean like STRef[1]? Because that is exactly what STRef does. It is worth noting that STRef is significantly safer than similar rust constructs because they deal with reference cycles. On the other hand implicit resource freeing is more awkward and you still need a gc.

This allows you to write programs that are asymptotically more efficient through mutability. Though there is a class of algorithms where lazy pure implementations gain the same benefits as impure ones using the sharing/implicit mutation that call-by-need provides.

There is an experimental linear haskell branch which might be merged into ghc proper eventually. It is stricter then rust, though, and prevents things like reference cycles which is more awkward but safer in places.

[1]https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-...

Re:Reflecting on Haskell in 2017 by megaman22 on Monday December 04, 2017 @09:23AM (#15843982)
Invoking an experimental Haskell branch is not what I would consider convincing.
Re:Reflecting on Haskell in 2017 by tome on Monday December 04, 2017 @09:28AM (#15844015)
Luckily that wasn't an important part of Tarean's comment then, and provided for interest only!
Re:Reflecting on Haskell in 2017 by wyager on Monday December 04, 2017 @08:48AM (#15843735)
> except for parts contained in Cell/RefCell/Atomic/Mutex.

This caveat is the problem. You lose a ton of properties when you have “interior mutability” (I.e. an object typed as immutable is, in fact, mutable). You probably can’t appreciate the benefit if you haven’t actually used a purely immutable language. Rust touts “fearless concurrency”, but it’s nowhere close to what you can get with Haskell’s guarantee.

> not powerful enough to write safe programs that are as efficient as C programs (since you can't modify memory)

Wrong on both counts. The vast majority of mutable algorithms can be expressed recursively and immutably. Consider haskell’s Vector library. It generates C-equivalent output a lot of the time despite being immutable and much higher level.

The one exception I’ve run into is algorithms that inherently rely a lot on random array writes (like in-place random shuffles), and in this case Haskell has the ST monad for externally pure, referentially transparent mutability that cannot escape into the rest of your program. The fact that haskellers almost never need or want to use ST is very strong evidence that, in fact, you don’t really need mutable semantics at all once you’ve figured out what you’re doing with immutable data.

Re:Reflecting on Haskell in 2017 by devit on Monday December 04, 2017 @09:43AM (#15844136)
Well, you could change Rust so that the names of types with interior mutability have to start with an "@" or something like that, and then it would be obvious, and could expose a trait to denote that (there is Sync and the internal Freeze, but they don't quite do that).

I think it hasn't been done because interior mutability is relatively rarely used, and being able to assert immutability in generic code is not actually that useful (and for things like hash map keys you'd need to implement Hash or other traits manually anyway, so you'll probably notice the issue).

In non-generic code, you have to call functions like "borrow()", etc. to access the internally mutable data, so you know it's mutable.

Regarding "fearless concurrency", the Sync trait ensures that, allowing "interior mutability" only if thread-safe (i.e. Mutex or Atomic).

Obviously you can have race conditions between modifications of different Mutex/Atomics, but that's an unavoidable fundamental problem: even transactional systems will have race conditions if you use two separate transactions where you should have used one, and single-threaded event based systems also have them if you "defer" part of the computation improperly.

Re:Reflecting on Haskell in 2017 by jerf on Monday December 04, 2017 @11:19AM (#15844973)
It would be better to rephrase the original point as "Some guarantees need to be embedded in the language itself, or otherwise you can't depend on them." Having the language itself guarantee immutability means that you can depend on it, because you know that no library will ever be written that expects you to pass things to it to be randomly mutated.

Rust has a different set of guarantees about mutability, but again, you want them embedded in the language so that you can depend on them, which you can. Even though you can escape to "unsafe" in either language, the rest of the affordances of the language prevent library authors from going too crazy violating guarantees.

That is, the important point isn't about "immutability", but about the guarantee.

Within the Haskell community, the idea that something like Rust may be closer to what we're seeking is perhaps controversial, but certainly not beyond the pale. And some of that controversy would be that Rust may not go far enough with things like linear types rather than the idea that immutability is the end goal. Personally, I think immutability was a sensible thing to try out in the 1990s, but we have legitimately made progress since then and continue to make progress.

(If you want to see a language where the 1990s beliefs about immutability really hurt it, check out Erlang. What Erlang ought to have had, in my 2017 opinion, is processes that have their own conventional mutable data storage, but with messages between processes incapable of carrying pointers/references. This would have accomplished their goal of total isolation and all communication being forced to go through copying, but without having the barrier to adoption raised by trying to teach people how to program in immutable languages, to what I found to be no great effect. Haskell does at least make some hay out of its immutability, in Erlang it is mostly just getting in the way. But back when it was written, the ideas were not as well teased apart as they are now. I don't mean this as criticism of the Erlang authors, because I think they did quite well, but this still would have improved the language.)

Re:Reflecting on Haskell in 2017 by spion on Monday December 04, 2017 @12:44PM (#15845766)
Haskell offers runST and runSTArray, which let you do mutation
Re:Reflecting on Haskell in 2017 by lmm on Monday December 04, 2017 @11:10AM (#15844889)
> My impression of Haskell is that the main issue is that laziness is simply wrong, since it causes space leaks that are hard to reason about, and seems generally less efficient than eagerness; furthermore, it seems rarely beneficial, so it seems more appropriate to explicitly request laziness rather than the opposite.

Agreed; Idris is strict by default and I expect other post-Haskell languages will be too.

> The next problem is that complete immutability is also wrong, because controlled mutable aliasing with linear types (like the system available in Rust) is what you really want since it's more general, efficient and expressive (allows mutable data structures), and complete immutability is just a special case of controlled mutable aliasing.

Disagree. Mutability is so rarely what you want that it's better to have immutability by default, and a clunkier syntax for mutable is ok.

> The third problem of Haskell is the weird syntax, that doesn't follow the common C/Java/JS or Python syntaxes for no good reason, making it hard to read and learn the language.

Yes and no. Curried-by-default is the source of a lot of Haskell's power, and that requires a certain amount of unusual ordering. The terseness of pointfree style is worth some learning time. $ is a big ergonomic advantage, though I'm not entirely convinced it's worth the cost. There are a few cheap wins, some of which Idris has (e.g. single colon for type annotations, standardised syntax highlighting) but you don't want to throw away the good stuff.

> And if one were to change these things in Haskell, the result would essentially be Rust (or more precisely, a future version of Rust once all the missing abstractions like HKT, specialization, etc. are added), so I think that's what one should use instead.

Agreed, but that thing doesn't exist yet, and the Rust that exists today isn't an acceptable substitute (it's 2017, I shouldn't have to use a language without HKT). For the moment I'd say Idris is as close as you can get.

Re:Reflecting on Haskell in 2017 by frankpf on Monday December 04, 2017 @12:57PM (#15845908)
>Yes and no. Curried-by-default is the source of a lot of Haskell's power, and that requires a certain amount of unusual ordering. The terseness of pointfree style is worth some learning time.

Correct me if I'm wrong, but you don't need Haskell's syntax to have automatically curried functions or a pointfree style. Using a JS-like syntax as an example:

    function add(a, b) {
        return a + b;
    }
    // functions could be automatically curried,
    // so you could do this:
    let add5 = add(5);
    
    add5(10) // => 15
    

    // "Normal" sum definition
    function sum(list) {
        return reduce(add, 0, list); 
    }

    // Pointfree version
    function sum() {
       // returns a function that takes
       // 1 argument (the list)
       return reduce(add, 0);
    }
Re:Reflecting on Haskell in 2017 by lmm on Wednesday December 06, 2017 @02:59AM (#15859372)
You can do that up to a point, but you pay a cost for it. It means functions are taking up a big chunk of your available syntax space, and it can make things ambiguous if something might be seen as both a function and a value. Look at how it works in Scala: much of the time you can call a function partially, but in an unsatisfyingly random-feeling set of cases you need to explicitly use "_" expansion.
Re:Reflecting on Haskell in 2017 by drb226 on Monday December 04, 2017 @01:59PM (#15846445)
"The third problem of Haskell is the weird syntax, that doesn't follow the common C/Java/JS or Python syntaxes for no good reason"

Haskell came before Java, JS, and Python. Perhaps it's their fault for choosing C-like syntax instead of Haskell-like syntax.

Re:Reflecting on Haskell in 2017 by MikkoFinell on Monday December 04, 2017 @01:50PM (#15846376)
Is it satire? Touting Rust while complaining that Haskell is hard to read? Please tell me you're joking.
Re:Reflecting on Haskell in 2017 by shmish111 on Monday December 04, 2017 @11:21AM (#15845000)
"for no good reason"? It come from the ML family of languages, that's the reason, is it not as good a reason as why Java has it's syntax? It's quite easy to read once you get used to it, no more difficult than the languages you mention.

Saying a particular paradigm is simply wrong seems like a very closed minded way of looking at things.