1. 66
  1.  

  2. 25

    This is why I’ve given up on Haskell a few times, a few hundred pages into a book like Haskell Programming from First Principals and I still can’t really understand open source projects or blog posts. So I (and I bet plenty of other devs) said “ehh, guess I’ll try Rust”.

    1. 13

      The most common kind of blog post about Haskell (and the most common kind of Haskell blogger) are the ones who want to push the type system to absurd extremes in the pursuit of mathematical masturbation. Not being able to follow all of those means nothing.

      Here’s a small calculator project I wrote in Haskell. Aside from parser combinators, there’s nothing fancy in there that shouldn’t be covered in a Haskell introduction.

      https://github.com/ninedotnine/happy-space

      1. 6

        The world would be better if people writing those posts modelled them upon notable papers like https://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf and http://gallium.inria.fr/~huet/PUBLIC/zip.pdf that always start with motivating examples and describe the solution clearly.

        1. 3

          The most common kind of blog post about Haskell (and the most common kind of Haskell blogger) are the ones who want to push the type system to absurd extremes in the pursuit of mathematical masturbation.

          This is what the original link is getting at, but my personal feeling is that there is a much deeper cultural issue at the heart of this. My experience is that Haskell specifically attracts people who have an interest in mathematics, or get enjoyment from mathematics.

          When it comes to programming there are many people like me, who some might say are at the software engineering end of the spectrum (versus the computer science end.) I appreciate the importance of mathematics and the enjoyment others can get from it, but Haskell is very much more compatible with these people than me. This isn’t a bad thing, but it means that there is a low likelihood that community and language will have broad appeal. Your contribution is a good example of what the community could aim for, but you are only one contributor.

          Not being able to follow all of those means nothing.

          This is all well and good, except that it means people will decide to do other things with their time which may be limited. Returning to where people are on the love for mathematics, or computer science to software engineering spectrums, they may find that Rust is a more productive experience.

          1. 2

            Haskell specifically attracts people who have an interest in mathematics, or get enjoyment from mathematics.

            I think this is almost certainly true.

            …which raises a question about both articles: what is the point of telling Haskell aficionados to write “simple Haskell”? Why would you advise somebody to “exit their ivory tower” if the “ivory tower” is exactly the reason to use Haskell in the first place?

            1. 1

              I think you highlighted that in your post. You talk about absurd extremes and provide a less fancy example. A good contingent of the folks coming to the community with the appropriate background are still probably not interested in the fancy stuff initially. I think the advice is reasonable because it’s really talking about creating material for others as opposed to the demonstrating the upper bounds of the language. Both have a place, but there probably isn’t a lot of overlap.

          2. 2

            Here’s a small calculator project I wrote in Haskell.

            BTW: why is the C code (shunting_yard.c) needed? How would it look in pure Haskell? Or does that repository contain equivalent implementations in both C and Haskell?

            1. 1

              That’s a relic from a first implementation. I ended up not going with it and rewriting everything in Haskell. Apparently I forgot to delete it, thanks.

          3. [Comment from banned user removed]

            1. 0

              This is a toxic comment.

              1. 11

                It is? Can you explain why? I think I must be missing something. “Toxic” is quite a strong term.

                1. 7

                  Here’s the different perspectives in which it can function (be read) in my opinion:

                  • Bashing people who struggle with Haskell and discouraging them from learning something else in the process
                  • Substance-less bashing of how approachable Rust is. Of course, critique is great, but just randomly bashing things without context or substance is simply toxic. It promotes a culture of pessimism, a culture which is extremely discouraging for a lot of people.

                  I’m not saying the person intended to be toxic, but the comment, even if accidentally, functions as such.

                  1. 1

                    Thanks, I see now where you’re coming from.

                    Substance-less bashing of how approachable Rust is. Of course, critique is great, but just randomly bashing things without context or substance is simply toxic.

                    Not being a dick, but I think a similar criticism could be applied to your original comment, which labeled something as toxic without explaining why 🙂

                    1. 1

                      I believe making people think about the things they’ve said on their own is important. If I just said “this is toxic because X” the poster would just go “oh ok I see why you’d say that” and be done with it, but if I just say that I think a comment is toxic, it forces the poster to think more deeply about what they posted. In this case, it was not at all too vague what the problem was with the comment and you can see this in the poster’s own, independent and thoughtful reply here.

                2. 5

                  I hope it isn’t interpreted that way. What I am saying is that Rust is a horrendously complicated language, so if Haskell is confusing then switching to Rust isn’t going to help.

                  I see now that it could be interpreted as ‘If something as simple as typical open source Haskell code confuses you, then Rust is definitely going to be hard for you, peabrain’ but I can assure you that’s not what I meant!

                  1. 5

                    For example C++ is also a very complicated language (and much more „dirty“ than Rust) and despite that, it is more accessible/acceptable for more people than Haskell. In the TIOBE Index, C++ is at 4. position while Haskell is at 41. with 0,240 %. The first one is Java, just for record. Or in the Stack Overflow survey, the most loved is Rust with 83,5 %, while Haskell is completely missing (which is really weird – or I missed something? Ctrl+F has not found any occurrence).

                    It has probably more dimensions that just complexity. Besides the complexity there is something like „mental compatibility“ or how to call it. And it seems that more people are compatible rather with „imperative recipes“ that with „pure math“. And they would rather deal with complexity than switch to a different paradigm.

                    Or what are other explanations, why there are not more Haskell projects and why more companies are not using Haskell? If this technology would provide more developer performance (implement more useful features in lesser time), less bugs or better maintenance, why would they not use Haskell? It would mean profit for them! One explanation is in the article above, it is about the learning curve and friendliness and accessibility of the community to the newcomers. But is it the main reason? Would it be enough to write simpler code and articles friendly to novices… and Haskell would then magically rise in the statistics or even become prevalent? Or are there other reasons? How to deal with them?

                    1. 1

                      No worries, I didn’t think that the comment was meant that way, but I think it’s worth the reminder that subtleties in tone don’t carry well over the internet, and many junior devs may read these threads and take comments like that to heart, eventually internalizing a sense of negativity/pessimism, either about other peoples projects or their own. And imo this is something that is very common.

              2. 23

                FTFY: “A plea to developers everywhere: Write Junior Code”

                Let’s get bogged down with how much simple code we write.

                God, I wish every developer would make an effort to write simple code.

                1. 7

                  I don’t disagree with you at all, but Haskell does have a bit of a spiral problem with these sorts of things; often folks writing even simple Haskell programs end up using very exotic types that are abstruse to more junior devs (or even more senior devs who just haven’t looked at, say, lenses before). I have this tweet about a simple dialect of Haskell saved because I think about this often when interacting with Haskell code.

                  1. 8

                    Those exotic types describe complexity that is present in other languages as well. However, in other languages, you do not need the type checker’s permission to introduce complexity. Instead, you discover this complexity after the fact by debugging your program.

                    It is questionable whether the Haskell approach is as wise as it is clever. At least to me, it does not seem very suitable for writing what the original post calls “junior code”. Consider some of Haskell’s main features:

                    • Purity and precise types:

                      • Benefit: You can use equational reasoning to understand the complexity in your code.
                      • Drawback: You cannot ignore the complexity in your code, even when it does not matter to you.
                    • Lazy evaluation:

                      • Benefit: It is easy to write programs that manipulate conceptually large data structures, but in the end only need to inspect a tiny part of them.
                      • Drawback: It is difficult to track the sequence of states resulting from running your program.
                    • Higher-kinded types:

                      • Benefit: It possible to abstract not only over concrete types, such as Int or String, but also over “shapes of data types”, such as List or Tree (leaving the element type unspecified).
                      • Drawback: Oftentimes, type errors will be an unintelligible mess.

                    It is ultimately a subjective matter whether these are good tradeoffs.

                    1. 6

                      often folks writing even simple Haskell programs end up using very exotic types

                      … abstruse …

                      🤔

                    2. 1

                      Isn’t a large aspect of Java and C# that they force you to write simple code? Then they get called “blub” languages or whatever. The reality is that you should write for whoever your audience is. Explaining everything such that a six-year old can understand it requires an inordinate amount of effort and without picking a target audience this is what your suggestion devolves into.

                      1. 7

                        Isn’t a large aspect of Java and C# that they force you to write simple code?

                        No. C# has had type inference, covariant and contravariant generics, opt-in dynamic typing as distinct from type inference, lambdas, value variables, reference variables, checked and unchecked arithmetic, and G–d knows what else I’m forgetting since at least the late 2000s. Java’s missing some of that (although less and less recently), but adds to it things like implicit runtime code generation, autoboxing, and a bunch of other stuff. Neither language is intrinsically simple.

                        But that said, I don’t honestly know that they’re honestly much more complicated than most languages, either. They’re more complicated than Go, maybe, but I don’t even know for sure if they’re more complicated than Python. The thing is that Java projects—at least, the “enterprise” ones for which the language has become famous—go crazy with complexity, despite—and often at odds with—the underlying language. There’s nothing preventing Python from doing absolutely crazy things, for example, and people who remember pre-1.0 versions of Django might recall when it used metaclasses and what would now be importlib to make one hell of a lot of magic happen in model classes. But the community rejects that approach. The Java community, on the other hand, is happy to go crazy with XML, factories, and custom class loaders to roam way into the Necronomicon of software development. I tend to regard this as the ecosystem, rather than the language, going to the extreme.

                        Haskell in practice, to me, feels like what C# or Java code taken to the extreme would look like. And there’s even indeed libraries like language-ext for C# or Arrow (which is for Kotlin, but same difference), which do go there, with (IMVHO) disastrous results. (Disclaimer: I work heavily on an Arrow-based code base and am productive in it, albeit in my opinion despite that comment.) This is also an ecosystem decision, and one that I think this article is rightfully and correctly railing against.

                        1. 5

                          There’s nothing preventing Python from doing absolutely crazy things, for example, and people who remember pre-1.0 versions of Django might recall when it used metaclasses and what would now be importlib to make one hell of a lot of magic happen in model classes. But the community rejects that approach.

                          I don’t think that’s true at all. The difference is that Python has good abstractions, so if you want to do something complex under the hood, you can still expose a simple interface. In fact, Python programmers would much rather use something with a simple interface and complex internals than the other way around. That’s why they’re using Python!

                          1. 4

                            I’m not sure we’re disagreeing, except for I think you’re implying that Java and C# lack an ability to expose something with complex internals and a simple interface. I’m logging off tech for the weekend, but Javalin is a great example of a Java framework that’s on par with Flask in terms of both simplicity and power, and done with 100% vanilla Java. It’s just not popular. And the reason I cited early versions of Django for Python is specifically because the community felt that that tradeoff of a simple interface for complex internals went too far. (If you have not used way-pre-1.0 versions of Django, it did Rails-style implicit imports and implicit metaclasses. We are not talking about current, or even 1.0, Django here.)

                            In other words, I think you’re making my point that this is about culture and ecosystem, not language in the abstract. Which is also why this article is making a plea about how to write Haskell, and not about abandoning Haskell for e.g. OCaml.

                            1. 4

                              Ah right yes I see about the Django thing. I was thinking about how it uses them now. I wasn’t aware it did import magic before, that definitely sounds a bit much!

                      2. 1

                        I used to use juxt and comp and partial quite a bit in my Clojure code, but these days I try to avoid them. They’re clever, they’re fun, they’re succinct… but they can also make it harder for the next person who comes along if they’re not already a Clojure hotshot.

                        1. 6

                          That’s setting a pretty low bar, isn’t it? Partially applying functions isn’t exactly whizz-bang fancy-pants programming in a Lisp.

                          1. 2

                            And yet, there’s usually another way to write it that’s more clear to someone not as familiar with Lisps.

                            (I’m not saying “never use these”. There are definitely times when it’s more awkward to use something else.)

                            1. 3

                              Function composition is the most fundamental functional programming concept as far as modularity is concerned, and partial application is not far behind. They are not specific to Lisps. juxt is slightly more “clever,” but nonetheless provides a ton of utility, is a part of the core library, and should not be shied away from. Talking about avoiding these functions without explicit examples or clear criteria is pointless.

                              Do you disapprove of any macro usage in your Clojure code? Are transducers out? What about core.async? I’ve seen more “clever” and confusing code written using those features than with any of the functions you’ve listed. For that matter, the worst (all?) Clojure codebases tend to be agglomerations of layer after layer of “simple” map-processing functions which are impossible to grasp in the aggregate and incredibly frustrating to debug. This is evidence of a general lack of coherent system-level thinking, versus any specific features in Clojure being responsible for complex, unmaintainable code.

                              The guidelines for writing clean, simple, maintainable code are never so straightforward such that they can be stated pithily, to the chagrin of Rich Hickey true-believers everywhere. It’s a combination of figuring out what works for a given team, adopting conventions and architecture well-suited to the domain, and choosing an environment and libraries to integrate with so that you introduce as little friction as possible (and probably more that I’m forgetting, unrelated to the choice of language). But picking and choosing arbitrary functions to eschew will not get you very close to the goal of writing simple code.

                              1. 2

                                I think you’re taking this a lot farther than what I actually said.

                                1. 2

                                  I’m sorry, I was trying to respond systematically to a comment I disagreed with. If you wouldn’t mind: how exactly did I take it too far?

                                  1. 1

                                    Well, I didn’t say “don’t use these”, I said that I “try to avoid them”. I don’t always succeed in that, and I’m happy to use them where they make sense.

                                    There’s a continuum between “can’t avoid it” and “totally gratuitous” and I try to push my personal cutoff towards the left, there. When it would make the code harder to read, I don’t avoid them!

                                    1. 1

                                      Well, I didn’t say “don’t use these”, I said that I “try to avoid them”. I don’t always succeed in that, and I’m happy to use them where they make sense.

                                      Why do you try to avoid using them? When does it make sense to use them?

                      3. 8

                        Thank goodness. I love functional programming, and I love the kinds of impressive cathedrals we can build, but I think they are also frequently unnecessary. Good clean simple functional code for most of the codebase is just good common sense.

                        1. 7

                          I’ve been doing advent of code in Haskell. I’m way behind (it’s over and I’m stuck on day 14). But one of the things that finally got me going with it was to give up on writing fancy Haskell and just write the program to solve the problem. I’m sure I’m doing all sorts of things wrong - my Haskell feels very Pythonic. But the answers are correct and I’m learning things along the way that I never would have picked up if I hadn’t written it the “junior” way first.

                          For example, I just implemented my first “instance” of a typeclass. I don’t quite understand everything behind the code, but I’m starting to unfold the ideas in my head by working with them. In fact, I think it’s important that I’m learning the “feel” of the code before the being able to name things. If you asked me to point out a lens in my code, for example, I would have no idea - but I’m sure I’m using several of them. Eventually the idea will click.

                          I was inspired to do this by a blog post I read recently where the author promoted the idea of teaching a thing by soliciting predictions about how it will work and how it will change, instead of teaching the “grammar” first. Kids learn their first language this way - we don’t know what future perfect tense is, but we know how to use it.

                          I read just enough of Learn You a Haskell to start programming AoC day 1, and I refer back to it when I need to pick up a new tool to keep moving. It’s been fun!

                          1. 2

                            I did some AoC days in Hakell, or as I like to say, baby haskell. Because I’m pretty new at the language, so I can’t use any of the advanced things. A certain someone looked at my code and asked me why my haskell looked like fortran though. https://github.com/cosarara/aoc2019/blob/master/day13/day13.hs

                            1. 1

                              Me too! I’ve only gotten through Day 7, because two toddlers and new job, but I enjoy fiddling around on smallish problems in Haskell.

                              1. 1

                                Hah, I hope you’re proud of that comment!

                                I do agree that the code is a bit hard to read, mostly due to all the integer constants (which I’m sure are because of the problem statement). It could be a fun exercise to gradually improve this (though the code is fine as is!). My first suggestion would be to move from some of those integers to dedicated types where it makes sense (e.g. enumerate the operations in a type Op, and write a function parseOp :: Int -> Op that will error on parsing instead of interpretation).

                            2. 4

                              I like to think my Hython project is mostly written in this subset, excepting the imperative flow control effect monad. However, I’m a bit biased here. :) FWIW I still strongly dislike using MTL even if I really like what it gives you.

                              TBH I recall being slightly embarrassed that I used a fairly vanilla dialect of Haskell. Nobody hassled me about this, but I think the issue is the intellectual distance between what is needed to get things done vs what the community gets excited about can be large.

                              1. 2

                                I think the issue is the intellectual distance between what is needed to get things done vs what the community gets excited about can be large.

                                Maybe it would be useful to clearly declare what is the goal. Whether it is implementing the software that fulfills its business requirements („get things done“) or something else.

                                1. 1

                                  This is really nice Haskell.

                                  1. 1

                                    Thank you! It was a fun project.