Hacker News new | ask | show | jobs
by lacker 2109 days ago
So what killed Haskell is the parochialism, the inability to address the needs of the Enterprise.

I don’t think so. What killed Haskell in mainstream programming is its weird syntax. Rust looks like C so it passed that first sanity check. People hate that something so aesthetically basic could define the success of programming languages, but it seems pretty clear at this point. Languages have to basically look like C in order to be popular.

7 comments

What killed Haskell is its obsession with monad-as-burrito tutorials that had nothing to do with getting anything useful done, as well as considering writing a PDF in Latex as an acceptable substitute for a blog post or documentation.

Nothing in Haskell was sufficiently interesting to justify overcoming the conceptual barriers and different culture.

The issue with monad tutorials is that they start at the top - trying to impart a generalizable understanding of the concept (which, short of category-theoretic explanations, requires hopelessly leaky analogies) instead of focusing on the purpose and usage of specific monads.[1][2] Abstract concepts must be first made concrete in order to understand them; in the case of monads, it's best to just look at the type signature for specific monads' "bind" (>>=) functions, as well as examples of usage, while actually using them in (permissively typed) code, rather than trying to connect burrito analogies to real life.

[1]:http://dev.stephendiehl.com/hask/#eightfold-path-to-monad-sa...

[2]:Regular expression tutorials, by contrast, virtually never attempt to explain regular languages or automata theory, which is why nobody complains about having to learn formal language theory in order to use `sed`.

A surprisingly digestible (and concrete) explaination of monads (Ch 2): http://www.cse.chalmers.se/~rjmh/Papers/arrows.pdf

This

They always go for the mathematical way of explaining stuff (this is not a compliment). Programmer English please, not Alien Math. (And that's me saying as someone who likes Alien math in general. But I just don't see the point in using it when programming).

Ok cool, your programming language now has two worlds, the functional world and the imperative world and they have two different syntaxes and you have to pass your world as an extra parameter and etc etc

And I personally find the syntactic sugar built on top of it confuses more than helps (ok, some of it is just Haskell being Haskell, but it confuses)

Speaking of alien math. I always thought Haskell looked like the countdown sequence in Predator when the Predator sets the self-destruct sequence. It was very off-putting and scary when I first saw it.

https://youtu.be/l2qZZQxMfmc?t=26

Here is an another reason it failed... I have a CS degree and I barely understand what was said here. just being honest... maybe I just need more coffee.
Purely anecdotally, I've found haskell-centric spaces to be pretty hostile, which definitely did not help getting me interested.
Interesting. Would you mind expanding your anecdote, explaining which spaces and what you found hostile? Knowing that would help me understand how to improve the community. Thanks.
/r/programming, /r/haskell, this site, interactions with real people at Haskell meetups and normal programming meetups.
I'm very sorry for your bad experiences.

If you are comfortable giving some examples of that bad behavior and what it looked like I'd be happy to watch out for it and call anyone on that bad behavior so others don't have to experience it.

I just noticed this comment. I appreciate your and tome's obvious concern for the community you're a part of, and I hope very much that you both continue to enjoy it and thrive. I mean you no ill-will and wish you best of luck.
> Rust looks like C

I program in C for a living, and no, Rust doesn't look like C. In fact, one of the things that keeps me away from learning Rust is that it is so complicated in terms of syntax.

Today I was reading this HN entry (https://news.ycombinator.com/item?id=24404628) and there is a little Rust code snippet towards the end, and I was thinking "this is so fck'd up, I'll never grasp Rust".

> Languages have to basically look like C

I wish they do.

Rust has more syntax than other languages, but it's a cleverly designed language: every bit of syntax has one unique, clear meaning. Every bit of Rust syntax is intentional, and the official Rust documentation helps explain the reason behind it, with the reason usually being "the programmer is forced to make a choice that is usually implicit in other languages, and we want the code to explicitly show the choice the programmer made".
I love the intention and the motivation behind the reasons they choose to define new syntax, but in what syntax regards, I tend to prefer less, not more. To me, things aren't clearer by adding more text or context.

"...perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away..." - Antoine de Saint Exupéry

You're overthinking it. Rust's syntax is not that complicated, even if it looks a bit scary before you read the book. For C programmers the real hard part is in semantics of references, which C programmers mistake for general-purpose pointers, and gloss over ownership.

Rust has an intentional well thought-out approach to what it makes explicit[1], which is useful in a language that is focused on control, correctness, and performance. It's not trying to make source code beautiful, it's trying to balance usability of the language with avoidance of surprises caused by implicit compiler magic.

[1]: https://boats.gitlab.io/blog/post/2017-12-27-things-explicit...

> Rust is ... so complicated in terms of syntax.

> there is a little Rust code snippet towards the end, and I was thinking "this is so fck'd up, I'll never grasp Rust"

After you mentioned it, I had to try to read it, and not programming in Rust, I'm surprised that there was a need to "clone" the "references" to the single value which would be accessed by each spawned thread using the "Arc" "reference counter."

A question for those who really know Rust:

As it is a single value, in C or C++ I could do the atomic access without any "cloning" and doing "reference counting" with something like "Arc" and it would still be safe. What am I missing? Was it necessary in Rust or was it an example of a demonstration that did more than actually needed?

For me, it's always more than just syntax that is to understand.

In C++ terms, Arc is a shared_ptr. clone bumps the reference count up. You only need to do that if you want another owner. If you don't, you can take a regular old reference to the contents of the arc.

The core issue here is that it's not clear (from the snippet, but also to the compiler due to the type signatures) when the threads join. If they're joined in the same scope, they could use "scoped threads" instead, which would remove the need for the arc alltogether.

> it's not clear (from the snippet, but also to the compiler due to the type signatures) when the threads join.

Thanks. Assuming the join indeed immediately follows the snippet, what were the needed changes in the type signatures in this case? I’d like to know the “good” and “idiomatic” example (which avoids doing more than needed) for that example.

    use scoped_threadpool::Pool;
    use std::sync::Mutex;
    
    const N: u32 = 3;
    
    fn main() {
        let mut pool = Pool::new(N);
    
        let data_mutex = Mutex::new(vec![0, 1, 2, 3, 4]);
        let res_mutex = Mutex::new(0);
    
        pool.scoped(|scoped| {
            for _ in 0..N {
                scoped.execute(|| {
                    let mut data = data_mutex.lock().unwrap();
                    
                    let result = data.iter().fold(0, |acc, x| acc + x * 2);
                    
                    data.push(result);
                    
                    *res_mutex.lock().unwrap() += result;
                });
            }
        });
        
        println!("{:?}", res_mutex);
    }
I kept the variable names similar to hopefully make it easy to see the transformation. Here, instead of using thread::spawn, we use a scoped threadpool. Effectively, rather than saying "take this closure and run it on a thread" like thread::spawn does, this uses "scoped.execute", which is like spawn, but ties the lifetime to the variable "scoped". This lets the compiler understand that all of these threads will be joined inside the given scope, and so it's able to grok the lifetimes. That variable is zero-sized and so will compile away to nothing.

(You still need the mutexes because multiple threads are writing to the same variables at the same time; imagine if we didn't push the result onto the vector; we could drop the mutex around it entirely, which would simplify things even further. See the example here, where because we are accessing disjoint parts of the vector, we can use no mutexes at all: https://crates.io/crates/scoped_threadpool

Thanks!

> You still need the mutexes because multiple threads are writing to the same variables at the same time

I understand that for the "data" for which the push exists. Do we however need it for res_mutex when the value that we want to update is a single (I guess integer) variable for which atomic add could be performed? Is there something like atomic add?

I think the snippet is part of the example code for Mutex. Here: https://doc.rust-lang.org/std/sync/struct.Mutex.html

After "It is sometimes necessary to manually drop the mutex..."

Ah ha! I should have recognized it, given that I'm pretty sure I wrote that, haha.

Incidentally, this is one reason why I've been advocating for a return of scoped threads to libstd; I can't change this example to be the "good" one, because we don't refer to external packages and we don't have scoped threads built-in.

Clojure does not look like C and requires some pretty tough mind-bending, especially if you have been programming for 30+ years like I did when I started. Still, more people use it than Haskell. But it has an enterprise-friendly environment (all Java or JS libraries, or both) and a super-friendly community.
Come to think of it you could take the arguments made here and replace Haskell with Perl, and then also replace Rust with Haskell and it would still make more sense.
I worked with a perl backend for years. The frame work was very good but perl was horrible. I hate the syntax and really hate having to reference and dereference. It is not that hard to do but it's just so annoying.
Perl was mainstream and exists in many corners. Haskell was never mainstream.
except that 15 years ago, Perl was everywhere
I disagree that syntax killed Haskell, but I agree that the Haskell syntax is particularly bad.

First, it’s the first language I’ve used in any capacity where I can’t mentally map the syntax. The orders of precedence are way too complicated, and the end result is that I end up blindly throwing $’s at the code until eventually it works. I have no idea why it has to be this hard.

Second, indentation. Seriously, indentation. Haskell is one of those accursed languages, along with Python, where indentation is apparently just utterly broken. I have yet to see A Haskell editor that can offer much more than the ability to cycle through the possible indentation levels given the context. It’s maddeningly bad, and often most editors can’t clean it up after the fact either.

Does python look like C? Its doesn't to me but w/e
This is exactly what happened to me: I did not take a further look on Haskell just because its syntax seemed so weird.
That's like saying you don't want to learn things you don't already know.
With that kind of attitude, you're not going to learn very much. You should take the opposite approach: be more interested the weirder the thing is.
Being interesting is not enough when you are picking a language for your next major project.