Hacker News new | past | comments | ask | show | jobs | submit login
Software Development Languages: Haskell (fosskers.ca)
96 points by colingw on Jan 31, 2022 | hide | past | favorite | 40 comments



I wish there were more articles like this. So much content online on programming languages go trough the basics of the language itself, with very "forced" code examples to showcase some feature of the language, and while they're great for what they are, they don't tell me anything about "what does the software development life cycle actually look in this language?" (including tools like package managers, IDEs etc etc)


I agree. In another forum someone was asking what level of competency you should be at with a language to put on the resume. Most responses were related to the language itself, but I argued it was more about the knowledge around the language. Knowing testing, tooling, packaging, and standard library is way more important than syntax. In turn, I think it's easier to write an article about syntax since its likely to be correct forever. While an article about dev life cycle is much more tied to the time at which it was written.


> Haskell is in the best-of-class category when it comes to documentation. Haskell's type system quickly earns your trust, and once it does, you almost never need to read someone else's code in order to understand it; you can just read the type signatures shown in the docs and get on with your life.

I don't think this is an accurate representation of how the Haskell community itself views the state of Haskell documentation. A large section of the community views the state of documentation as very not best-of-class and strongly advocates for improving it (and views type signatures as a poor replacement for at least examples). See e.g. https://www.reddit.com/r/haskell/comments/2ory86/there_is_a_...

Likewise "Otherwise, toss a coin!" as a response to Cabal or Stack makes me sad. Tooling can often depend on exclusively either one or the other (see e.g. https://plugins.jetbrains.com/plugin/8258-intellij-haskell) and the fact that from a capability point of view, they're converging on the same thing makes it an unnecessary point of friction for new users, although I understand the reasons for why they both came to be.

EDIT: I should detach myself from the usual curmudgeonly HN comment to say that the organization of the article is very well done. I would like to see how non-Haskellers approach it because I think stuff like "What language idioms are available?" is a good way to approach a new language.

EDIT 2: The original Reddit link was far older than I thought it would be. Here's a more extreme (more than I personally agree with) take on the situation, but is a lot more recent: https://old.reddit.com/r/haskell/comments/or93z3/what_is_you...


> I don't think this is an accurate representation of how the Haskell community itself views the state of Haskell documentation. A large section of the community views the state of documentation as very not best-of-class and strongly advocates for improving it

Almost every time (though it's quite rarely) that I end up on a Haskell documentaiton page, I facepalm and move on. Yes, there's a dump of inpenetrable types. But... how do I actually use those types? :)


I think this is a very insightful comment. If the documents showed:

1) a simple instance of use of the language feature

2) a few typical instances of use

and

3) did NOT use towers-of-hanoi, prime-sieve, mathematical smartness, pointless tricks unrelated to the core functionality, dubious features under dispute..

The best UNIX man pages for systems commands do exactly this. They show all the commandline options in the sort-of BNF, they list the meaning/intent of the options (hopefully alphabetically) and under EXAMPLES they show you the 3 cruicial commands you really came looking for, exactly as the worked when the manual page was written.

I know I used man -s 1 or -s 8 to exemplify how to do man -s 2 and man -s 3 pages, but the point is they obey some simple (BNF) norms, some simple (nroff -man) format and EXAMPLES exemplify real-world use. Not "I am smart, watch me put a fruitloop in my nose" but real world, applicable instances.


If I compare Python docs and Haskell docs even the official python docs are completely terrible. For example let's take the regex library and want to look up how to get match groups: https://docs.python.org/3/library/re.html

99 out of 100 times someone will land on this page and already know what regexes are. So basically 1/3 of the page is useless. After a while you find you need to use the `match` method. Great! Now I see I get a match object. How do I deal with those? There is not a clickable link here. In the example it's an opaque object. So I scroll further maybe ctrl+F to find match object. Here I find what I'm looking for. So this take me pages of scrolling and then manually searching for the object a method returns. That SUCKS. Everytime I use the re library and need to refresh my knowledge I'm in pain.

Compare this to Haskell: https://hackage.haskell.org/package/regex-compat-0.95.2.1/do...

Great. I instantly see how to construct a regex. The next thing I see is the `matchRegex` function. The bread and butter of using regexes. And what does it return? "Maybe [String]". Literally couldn't be easier then that.

Of course this doesn't hold for every Haskell library and Python library. But this is my general experience.


I think, coming from other languages, it takes a while to really absorb how much you can rely on types to tell you what a function is doing - to the degree that for some functions you can infer the implementation from them[1].

That said, Haskell documentation tends to be really lacking on some of the basics that other ecosystems get right, like where to start, shielding beginners from functions for more niche usecases, examples of how to build or deconstruct values, or end-to-end case studies. Some of the nicest things to _use_ have an API that primarily comes from typeclasses in base, and don't provide any documentation beyond a list of typeclasses. My impression has been that most Haskellers seem to be on the same page that those things need work, though - I'm optimistic about the situation improving, but it's still hard to recommend to anyone expecting the experience of working with popular libraries from more mainstream ecosystems.

[1] https://bartoszmilewski.com/2014/09/22/parametricity-money-f...


Cabal and stack are also very hard to understand when starting out.


Rust's `cargo` kicks the crap out of either of them.


I learned Haskell a few years ago, enough to read it, but not fluent enough to comfortably write it. Haskell scratches a real intellectual itch, related to how it formally captures certain concepts (types, purity, etc). But my opinion is that Haskell collapses under the weight of its terseness and academic complexity. Can it be extremely elegant? Yes, in contrived examples[0]. Is it able to maintain this elegance when dealing with common problems? No.

0. https://wiki.haskell.org/Introduction#Quicksort_in_Haskell


> I learned Haskell a few years ago, enough to read it, but not fluent enough to comfortably write it.

Several years ago I tried to learn Haskell, and while I got to the point where I could write Haskell code, I found reading it nearly impossible. Even code I'd written a day or two earlier I found extremely difficult to decipher. While I learned some interesting things from the time I spent trying to learn Haskell, I eventually gave up on ever actually using it.


Do you maybe have an example where it isn't able to maintain that "elegance"? In my experience of writing Haskell, I do write pretty terse, higher order code, with lots of higher order functions, applicative functors, monad transformers, and many other abstractions that relatively tersely describe what is happening.

The expressions may not be simple, because what is happening may just not be that simple, but (purely a feeling) compared how the code achieving the same would come out on other "mainstream" languages, Haskell still tends to come out on top for me.

Make it clash, which is a (strict, real) Haskell subset replacing VHDL and Verilog[1], and the difference is orders of magnitude. In my hobby use, I plain refuse to write anything else than simple glue logic in VHDL or Verilog now.

[1] Usually compiling to them.


How is developing with Haskell? I've been considering picking it up because I love functional programming. I usually write quick n dirty stuff in python and lately I've been picking up go as well, which is a breeze to write with.

Can I get moving quickly with Haskell or will it take a while to get accustomed to it switching from other languages? My fear is that, like when I started picking up rust, it'll be a bit of a slog.


> will it take a while to get accustomed to it switching from other languages

If you've never dealt with a language similar to Haskell, I think the learning curve is very steep. But it was one of the most worthwhile things I did in my programming life.

Or not, because now I'm stuck with a view of how the world could be, which at least in my professional work life is pretty much out of reach. (Might be different for everyone individually.)


> how the world could be

I already feel this way about Clojure vs other languages I know, or Ruby (what I get paid to do) vs Python (what I use when I must).

It gets tiring to know elegance and terseness and then have to use wooden clubs.

If Haskell is some steps further up the ladder, but opportunities to use it are very limited, it would make me even more frustrated.


> I do write pretty terse, higher order code, with lots of higher order functions, applicative functors, monad transformers, and many other abstractions that relatively tersely describe what is happening

Can other people understand what is happening? Can people with less expertise understand what is happening? Can you yourself understand what is happening 6 months from now?


Does a junior developer understand the core abstraction of a given project written in any language? Chances are the answer is no. Haskell just expresses that inside the language, while other languages tend to rely on design patterns, or just simply distributing the logic between many parts, which are arguably more error prone.

But for all practical matter, they can better write a single isolated case because it has to abide by stricter typing rules, instead of only conventions that supposed to he upheld.


Yeah, better than in other languages I'd argue, as the meaning of the code is not obscured as much. This becomes especially (but not exclusively) clear with clash vs. VHDL/Verilog.

You have to actually know Haskell first, obviously.


I also want to add: That was actually the point of my comment. "Applicative functors" may sound scary as a name, but the concept really does help in keeping things readable.


I've worked on commercial Haskell projects and it was actually quite nice! Tooling was a pain, libraries may be lacking, but describing a complex domain as a state machine in the type system with automatically generated tests (via properties) and formal methods to prove some of the core algorithms was a really interesting way to develop. Not sure it'd be my pick for my own startup though.


What formal methods did you use?


Combination of Agda, Isabelle, Coq, and TLA+ depending on the team - I mostly interacted with TLA+ and the compiled Haskell from Agda specs :)


Wow definitely want to pick your brain. How large/# of LOC were the Agda, Isabelle, and Coq specs? What industry was this?



It seems to work pretty well for the things I've used it for. There are some problems that are better served by an imperative solution with in-place mutable array operations and so on, but the ST monad works pretty well for that.

I think that's one of the things that tends to take a long time to learn: Haskell imposes a lot of limitations up front on what you can do. The rules (and in particular the barriers between pure and impure code) aren't quite as rigid at they seem. There are ways to work around them without actually breaking any of the soundness rules.

(To be fair, there is one well-known foot gun, which is that if you lazily read from a file and close the file, the consumer of the file's contents will often get truncated results. So don't do that. Maybe it's been fixed by now, or that API's been deprecated? I haven't really kept up on current events in the Haskell world.)


> (To be fair, there is one well-known foot gun, which is that if you lazily read from a file and close the file, the consumer of the file's contents will often get truncated results. So don't do that. Maybe it's been fixed by now, or that API's been deprecated? I haven't really kept up on current events in the Haskell world.)

I think the API is still there, but lazy IO is so heavily discouraged that no one is using it. It's not relevant anymore and considered a mistake.


My problem with it is that nobody actually writes Haskell itself. Every real usage has N different language extensions, and everybody is writing with a different subset of those.

Beautiful language though, wish I could use it at work.


So what is "Haskell itself"? Writing everything with the Prelude and the `IO` Monad?

Sure some people go off the rails with some things, but it's possible to write mostly vanilla Haskell and get a lot done.


It might be worth giving a pointer on setting up your own stackage server. Otherwise, Haskell projects can become an ugly monolith sooner in a company's lifetime than one might expect. It can then become a double-edged sword that you're quite productive in the language.

The CI for a medium-sized Haskell-using company should have multiple instances of:

Project repo -> CI compilation -> tests -> publish library to company's internal stackage.

That way, when someone inevitably publishes something that breaks things, the other teams will have an easy mitigation / known-good-version to fall back on.

In other codebases (especially if they use the typical popular dynamic languages), you might start breaking things up into microservices sooner than you would with Haskell. But given that Haskell runs much, much faster than those other languages, and isn't quite the memory hog that the JVM is (when we're comparing to Scala), you might find that it'd help you scale if you broke up the codebase into libraries, rather than breaking it up into microservices. This is especially the case if your company / project is bottlenecked on ops (which many companies are, not just ones using Haskell). Multiple libraries is much easier to manage than microservices and forestalls the point at which you'll need serious dedicated ops.


In your opinion, is that still the case if the company uses a one-version monorepo? Ideally every test would break when the library team tried to commit.


Depends on how big the monorepo is. I’ve seen just two or three highly productive teams start stepping on each other in ways that were more par for the course at 200 person engineering teams.


Trivia: the recently posted PostgREST project was written in Haskell.


This is Part 3 of an on-going series about the usage of various languages for real-world software development.


I like the article much but I am missing some statements around IDE. What I like about typed languages is the code completion and refactoring it enables when used in a good IDE. Code completion also enables me to "explore" what is possible at the current location. Are there any good IDE's out there for Haskell?


I find VS Code with the Haskell extension to be very good for displaying type signatures, code completion and navigation, etc. Holes (which are covered by the article) are the go-to way to see what's possible at the current location. You type an underscore in your code and the compiler tells you a bunch of information about what goes there including things you could put there that would typecheck.


From a practical software development POV I think the biggest problem with Haskell is the laziness by default. It's the central theoretical motivation for the language but when it comes to real work there are so many ways it can bite you, which is ironic given how much Haskell prioritizes safety in almost every other aspect of the language.

To me it's always been hard to reconcile that you have to deal with so many restrictions in the language that save you from ending up in some ill-behaving program state, yet a wrong fold can basically blow your entire program up.


Is Laziness really that big of an issue in practice though? Admittedly, I'm pretty new to Haskell. I've done a few CLI programs and one little web app using Yesod, and the fact that Haskell is lazy by default has never come up.


It comes up when you're dealing with large amounts of data; you go to evaluate the value of a function and it crashes because the stack limit was exceeded. (Ghc changed some things awhile back so that it just allocates more memory now instead of crashing, but still it can cause performance/memory use issues if what looks like just an integer is actually just pointing to an unevaluated thunk pointing to megabytes of data.) Laziness is also annoying when trying to get parallel code to actually run in parallel instead of having expensive computations deferred to whenever the consumer evaluates the value of something.

Laziness does have real benefits sometimes though. It's nice to be able to define infinite data structures, for instance.


My experience:

For one, it makes reasoning about runtime (as in "how long") harder: E.g. an innocent looking "take 5" might be what actually executes half of the code you've written... which is also harder to reason about than if it would be "all of it". This can also make debugging harder: Values are only evaluated when they are needed, and a "value" itself may not be just, say, a single number, but a complex data structure consisting of many values, so that "evaluated" has many different stages between "a thunk, not evaluated at all" and "weak head normal form". So code does not get executed when you wrote so in your program, but potentially much much later. Semantically that should not make a difference (the results should be the same), but when you want to debug program flow...[1]

You can see how "thunks" in general add a certain dimension to everything you do. Throw in interaction with garbage collection, and things aren't always obvious in resource usage.

Also, there is maybe an argument that if you have infinite data structures that you only tend to evaluate partially (which is what makes laziness really fun, actually), then accidentally trying to fully evaluate such a data structure makes your program not terminate anymore. But I'm not sure how that's really a problem coming from laziness. Laziness allows you to handle infinite data structures in the first place, mishandling them is not much different than accidentally writing an infinite loop in a strict language, in my opinion.

That being said, I still enjoy writing Haskell very much when I get to. It makes me feel much more productive, and I do lament having to use other languages at work. I must say using Haskell was always on private projects, and I have never tried doing so in a team, though.

[1] On the flip side, I find the pureness and strictness of Haskell to make debugging easier, or just less necessary.


It can sometimes be an issue in certain "Gotta go fast" scenarios, where you'd otherwise be able to reason quite confidently about program behaviour with a strict language.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: