New Haskell Foundation to Foster Haskell Adoption, Raises 200k USD
infoq.com
infoq.com
I really like functional programming, but Haskell turns me off and without at least one major example of a beautiful, popular solution written in Haskell (and I don't think pandoc counts), I'm not going to make the effort to push through that barrier.
Other languages that occupy a similar space ("superior but tragically underused") have these examples. Erlang has...well a lot but Matrix and RabbitMQ come to mind. Clojure has...well it has datomic, but also Jepsen uses it, and heck I've used it and its fine.
The real question is, who's fingers itch to write haskell, and can you please pay them $400k to rewrite nginx in it?
I do really honestly believe that Haskell needs a "paragon project" like Matrix or Jepsen that has proven itself useful to the world without respect to its Haskellness, and then show that this solution was much easier in Haskell.
I keep getting down-voted on this thread, and I expect it to continue. Friends thought I was nuts to give up on Lost after season 1, or The Hobbit after movie 1, or the GoT books after book 3. After years of hearing much talk and seeing no walk, I'm giving up on Haskell. More than that, I'm going to call out anyone talking it up to give specifics because literally everyone I've every spoken to about it gives it a very high regard, but have never, ever written a line of it.
It's fine to make silly hyperbolic statements in a language war, but when you are trying to present yourself as the voice of reason.
There are amazing claims about how Haskell (or Lisp or Erlang or ...) give you these amazing superpowers and allow you to write immensely more correct programs immensely faster. That could be true (and I personally do believe it’s true). But then where are all of these immensely-correct fast-written programs? I don’t buy the pg-inspired secret-weapon lurking-in-the-shadows argument unfortunately.
In C, C++, Java, and Python—even Pascal!—it’s not even funny how many examples there are. It’s nearly limitless. The superpower languages struggle to come up with just a handful of examples.
What I personally observe in the Lisp and Haskell world is that people like to write purportedly useful “reusable” libraries, and nobody likes to write applications. A very select few are successful at these libraries (where success = broad adoption), but most of these libraries are intellectual games and puzzles that benefit almost zero “working programmers”. Lisp has plenty of libraries to do pattern matching (great!) but basically zero for putting up a native GUI.
(As a self-deprecating anecdote, my first instinct writing Haskell many moons ago was to create a library of all of the major abstract algebraic structures found in pure math. You can imagine how many people that library would benefit.)
A lot of this also has to do with not the language, but the tribal nature of programmers and their acceptance of other tools in their chain. The Linux kernel will never accept a patch written in Common Lisp, and a lousy middle manager will never let you write your domain logic in Prolog. Nobody wants to figure out how to integrate GHC into their CI/CD. Most people have little patience to figure out how to write good C code that integrates with Erlang’s FFI.
I’m being a little flippant in my description of the state of affairs, but I think the spirit of it is correct.
I think the commenter is right. If any of these languages will be successful, there have to be many, many successful programs written in said language where the language’s benefits tangibly pay off. Having one or two Hail Mary projects doesn’t really mean anything.
Almost zero people writing lens libraries in C, almost no people are writing state-of-the-art asymptotically optimal Cool Tree data structures in C++.
Almost all users of these languages are writing an application that isn’t designed to serve the intellectual needs of the programmer themself.
I thought your point about bootstrapping to popularity was a good one. It's not easy but I'm convinced that Haskell is good enough that it's possible (in fact I think it's inevitable that either some Haskellish language will become popular or some popular language will become Haskellish).
One of the problems with languages like Haskell, Erlang, and Lisp is that they’re so flexible that you often don’t know what you’re getting into when you read somebody else’s codebase. You often see projects written in these languages with a small core team that understands e.g. SBCL and the various macro libraries they use to get work done, or Haskell and the monad transformer stack they use in their app, and it’s very hard over the long-term to on-board new team members and bring them up to speed at a rate which keeps the team healthy.
Maybe this is an inherent problem with “flexible/powerful” languages.
There are a few Haskell tools around that people do regularly use—Git Annex, although Git LFS seems to have edged it out in popularity, and Pandoc, which I’d say is powerful and irreplaceable.
Maybe zero people are writing lens libraries in C, but C does have a fair number of different string libraries around, exception / error handling libraries, and a bunch of people churning out terrible “single-header” libraries which only exist because average C programmers aren’t good at working with common, mediocre C build systems.
But everyone still shits on "Rust Evangelists" when they bring it up. You just can't win! :D
This is easy to measure because it refers to properties of applications (performance and vulnerability-density) rather than properties of the development process.
Rust also seems to reduce the need for a ridiculous guru at your company to help keep programs correct. That is easy to identify and measure. Haskell doesn't seem to do this. If I imagine running a team of 80 people on a Haskell application vs a Java application, do I feel like I'll need fewer Haskell gurus? No.
Rust was sort of co-developed along with Servo with the intent to be used by Mozilla. For all the others using Rust, it solves immediate problems that people have been having for years. When you're going up the learning curve and fighting the borrow checker, you can rest assured that the pain is in exchange for eliminating entire classes of bugs. That is an unusual situation in the world of languages.
You can optionally choose to use a Rust-based HTTP stack.
librsvg is one.
I think it was originally written in C, and it apparently approaching being fully Rust.
Rust claims very loudly, repeatedly, and apparently with honesty that "gotta go fast" and safety are language design goals and features. So I'd imagine even a developer who just wants to get their hands dirty with Rust would be willing to do battle with the borrow checker since there's a good chance they'll eventually hit those goals that they share in common with the language itself.
Does Haskell have "gotta go fast" as a central language feature? If not, that'd be a real gamble for an SVG renderer. After all, what's the use of a library that produces the output you'd expect, but also at a speed you'd expect for a kludgy, over-engineered, text-based vector image format?
> But everyone still shits on "Rust Evangelists" when they bring it up.
Yes, but the language forces them to shit on it for reasons other than "not being fast." :)
Edit: clarification
Ah, the Fermi paradox of programming languages. I wonder what the appropriate terms should be for the Drake equation of language adoption? I definitely think there is a factor for users originally attracted to shiny new objects, who then discover shiny... Oooooh, Idris!
Shellcheck has become a very important tool to many people writing shell scripts: https://github.com/koalaman/shellcheck
GitHub's semantic analysis is written in Haskell: https://github.com/github/semantic
I'm not sure why Pandoc doesn't count.
I think it is definitely the case that most examples of successful Haskell are technical programs of interest to hackers. I don't think that's much of a problem myself.
If you're writing some boring CRUD app, or a GUI program that makes a bunch of syscalls, Haskell sounds like a giant pain in the ass.
From that POV, I think that Haskell's relative lack of popularity is perfectly fine. Not every language has to be maximally general.
It seems to be a common view point that Haskell makes IO difficult. I actually personally found that it was more powerful because I was being more explicit about it. It also allowed for higher ordered functions to abstract common patterns in IO. The only difficulty in Haskell was not due to monads or type safety, but rather in getting my brain to understand lazy evaluation by default.
I found this blog post to be kind of interesting: https://blog.ploeh.dk/2017/02/02/dependency-rejection/
Skim that and then take a look at the back-and-forth in the comments.
Sometimes going out of your way to write mostly pure (or just non-IO) functions can make your code more difficult to reason about. In my experience, you're sometimes left with two choices: either do a bunch of IO upfront that you might end up throwing away (if there is an `if` statement in your pure function that determines whether you need the result of some IO op), or pull out logic into the naughty impure edge of your program.
If you do the former, you're wasting performance. If you do the latter, you're not actually fixing anything that pure functions are supposed to help you with, because you still have logic in impure functions.
In my experience there are these edge cases where finding the cutoff between "the edge where I'm allowed to do IO" and the "pure business logic" is not that clear or easy.
Also, as the old saying goes: monads don't compose well.
I agree monad composition isn't amazing. When I was writing a lot of Haskell (I was working on real time 3d reconstruction algorithms from video feeds using camera input and OpenGL output) it wasn't a very large roadblock though. But effect type systems seemed like they might be cleaner.
I don't have time to read through that article right away but I saved it for future reference - thank you for sharing.
[1]: https://www.destroyallsoftware.com/screencasts/catalog/funct...
Most CRUD server handlers basically boil down to fetch and parse incoming HTTP request, decide to fetch some external resources (db/cache/service), prepare a response, and reply to the client. Fits the IO-on-the-outside model perfectly.
React and friends have shown that UI views are mostly pure functions. Some local state and external resource can be fetched in some runtime hook and passed into your pure renderer.
For the less simple apps, IO can be hidden behind interfaces for complex business logic. Here's an imaginary example:
def getPriceForShoppingCart(product: Product, user: User, getDiscount: (User) -> Percentage): Price {
if (user.isPremium) {
product.price * getDiscount(user)
} else {
product.price
}
}
How do we put the IO on the edges? Does this function just disappear and we have an `if` statement in our impure outer shell? That smells like business logic in the impure shell to me. Do we go ahead and call `getDiscount` in the outer shell and pass in the discount to the function? That also sucks because what if we made that trip to the database when we didn't need to (because the user is not a premium user)?I feel like every time I try to be religious about IO at the edges and not using impure dependency injection tricks, I end up running into a bunch of cases like this.
On the other hand, if I stand back and look at a function like this objectively... It's easy to understand, it's still quite easily testable. What's the problem?
Anyway, CRUD does really not lead to clear "is IO"/"isn't IO" boundaries, and the business logic isn't a good candidate for separating from IO into a pure core. Instead, it is a great candidate for separating into a "with business data" context that is neither simple IO nor pure.
On the implementation of that "with business data" context you will find plenty of opportunities to push IO to the boundaries of complex code that will become pure. But your intuition is correct, what you are missing is that the code can be broken on more dimensions than the ones you are assuming.
Sure, which is why there is no reason to code them specifically at all as unique applications. Instead, the domain isn't the business domain of a particular application but far more general, and then you code something like PostgREST in Haskell where the domain is mapping between database and API calls, and solve the problem generally, and define specific applications just with database schemas.
“Domain logic” is only “business logic” when you've decided you need a bespoke app built for a specific business problem, which makes sense when you have a complex business problem, but is simply wasteful when you have a problem for which a general solution already exists. It's like asking who is the best designer go go to for a one-off custom car for perfectly average daily commuting use: you've chosen from the outset an inefficient solution to your problem.
Facebook also wrote parsers, analyzers, and linters in PHP, and those were deployed to production. PHP let you write these things, but also all of the cruddy stuff non-Hackers like to write.
I am by no means advocating the use of PHP (even modern post-fractal PHP), but where’s a boring application that Haskell is a success in? You know, a little game you could download and have fun with? Or a billing system?
Here's a "boring" internal management system: https://twitter.com/fegundersen/status/1325534608079941633
Disclaimer: Founder of digitally induced, the company that helps make boring things with IHP+Haskell :)
It's possible and even very probable that PHP devs... stolen plenty of ideas from Haskell just to write "utility" libraries with witch they could write those parsers/analyzers/linters you are talking about :)
However, I also can't see how PHP could be used to efficiently re implement that spam library Facebook developed.
There whole promise lies on separation of effort into two teams: * spam engineers write pure code that happily merges data from different sources * backend engineers write integrations with Facebook systems and optimization passes over code spam engineers wrote to make sure that most optimal use of costly I/O was made
PHP brings nothing, literally nothing to the table here. While Haskell have all the mechanisms already built in.
Your argument about PHP sadly boils down to "its all Turing complete anyway".
I've being thinking a lot about this, and I think this is better addressed by using the same standards as in other fields. Namely, a scientific study. I imagine an environment like repl.it, with just a simple text editor, and some problem description and a set of compressive tests that serve as a spec. Then we measure time to produce a solution and number times the solution makes the tests fail (bugs). With enough people doing this, and in enough languages, we might be able to reach some conclusions. Until then, we will be stuck in the dark.
[0]: https://www.cs.yale.edu/publications/techreports/tr1049.pdf
IHP has around 230 monthly active projects at the moment, and it's growing fast. It was released 8 months ago and now is already the second biggest haskell web framework :)
I think haskell has been previously tragically underused because it was missing good documentation and pragmatic solutions that help you to get things done. Atleast in the web dev space this has changed this year. Watch our IHP demo video and you'll get it: https://www.youtube.com/watch?v=UbDtS_mUMpI
Disclaimer: Founder of digitally induced, the company that makes IHP.
No, the best course of action is to solve problems that haven't been solved, not to do marginal reinventions of the wheel.
A killer solution is definitely a help in getting a language ecosystem to take off, but those are rarely simple reimplementation of an existing tool. Occasionally they are in a recognizable existing category, sometimes they are category-defining.
API (CRUD/Auth) code written in HS becomes a beauty once you start building experience with HS. I think the most advantage of FP comes from how it changes the way you model your solution for a problem. With a strict type system, it's easier to anticipate edge cases and with currying building abstractions becomes natural.
Having said that, HS is not all sunshine. It took me an inordinate amount of time to setup an IDE like environment. If I recall correctly, HLS would take > 20GB of RAM in few hours with our code base. Eventually I had to remove that and extensively use only the editor features to jump around code.
For an Haskell implementation to have any efficiency, it must re-order the order of execution inside of function calls as it sees fit, which it can do due to the non-strict semantics. This is fine in theory, but dynamic libraries aren't built on that assumption; they're, naturally, built upon the idea that they are a black box and that the consumer simply calls the function, obtains the result, and does not care nor has access to the insides.
A Haskell implementation must have knowledge thereof, thus, the practical effect is that with Haskell it's impossible to, say, fix a security problem in a dynamic library and simply have all consumers seamlessly benefit from that fix — every consumer must be recompiled with the new library's source.
And this problem is very seldom raised in discussions — dynamic libraries are fundamentally designed on the assumption of strict semantics.
It's about dynamic libraries written in Haskell itself.
Try compiling a Haskell program in GHC with the `-dynamic` flag, and then update any of the Haskell libraries it is linked against, after this, the program will fail to start with a linker error, and must be recompiled itself.
https://www.reddit.com/r/archlinux/comments/7jtemw/which_pac...
See this discussion there which explains why, in essence, dynamic linking is not a feasible route with Haskell. Arch Linux currently does this, or at least at the time of writing there, which leads to all Arch Haskell packages being required to be recompiled and reinstalled, if but a simple library be updated that they all use.
Non-strict semantics would be prohibitively slow without these optimizations.
You won't find a Haskell compiler that doesn't do this.
It could do so if it were to be willing to sacrifice having even a modicum of performance.
Lazy evaluation is incredibly slow without these. The theory behind lazy evaluation, is that it can gain back some of the performance it inhærently loses by allowing these kinds of aggressive hole-program optimizations because code can freely be re-ordered as no code has side-effects.
Without taking advantage of this re-ordering, one is only met with the pœnalties.
Note that in Haskell very fundamental primitive logical operations such as `||`, `&&` and `if` receive no special treatment from the implementation and are ordinary functions. If these were not allowed to be optimized as such, and they would be provided as a library, then the code would be prohibitively slow and would essentially require that a thunk be passed at every instance of a normal logical construct.
> The archlinux haskell packagers could just ship programs as statically linked binaries and not recompile them on each minor dependency upgrade (except if there is a security upgrade). The binaries would not have any dependency on haskell packages themselves and you would not be forced to upgrade the whole package graph when some dependency is bumped, that would also save a lot of download bandwidth. I'm not sure that anyone really needs haskell packages (besides standalone programs) from the system package manager. No haskell dev I know builds their programs this way.
Indeed — no one does, because the unsung pain with Haskell is that it's impossible.
With strict languages one has the option for dynamic linking, and dynamic linking seems to be what has won out due to it's convenient “update once; update everywhere.” benefits — that is not available to non-strict languages, for which static linking is the only viable way of distribution.
Citation needed? Something like 20% of the reason I gave up on Rust was precisely that there was no documented way to force it to generate a static executable.
If you want to have a 100% static executable, that's been possible for a while now: https://doc.rust-lang.org/edition-guide/rust-2018/platform-a...
> https://doc.rust-lang.org/edition-guide/rust-2018/platform-a...
Yes, I'm aware of that; the fact that they continued using glibc as their default (aka canonical) OS/syscall interface after discovering that it was so impossible to statically link that people had to retarget code to a whole second libc was one of the final nails in the proverbial coffin for me.
> because Haskell also links dynamically to glibc by default, but it statically links with other Haskell code.
Not particularly relevant, since my inquery was mainly about Rust, but on my system I get:
$ echo 'main = pure ()' > foo.hs
$ ghc foo.hs
[1 of 1] Compiling Main ( foo.hs, /tmp/tmp.icUr2pqZXx/Main.o )
Linking foo ...
$ file foo
foo: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped
Admittedly, it's entirely possible that I fixed ghc and/or gcc at some point and forgot (I've fixed other bugs, but I don't recall fixing this one).I think you've fixed something. I get
$ file foo
foo: ELF 64-bit LSB executable, x86-64, version 1
(SYSV), dynamically linked, interpreter /lib64/ld-linux-
x86-64.so.2, for GNU/Linux 3.2.0,
BuildID[sha1]=f9e391e2e7da2c80dbfc7820ac87294ef447c57a,
not strippedDue to Haskell's non-strict semantics, if a dynamic library, written in Haskell, that some Haskell program is linked to is recompiled, then the code that is linked to it must also be recompiled.
For instance let us say we have pseudocode:
let list = [1,2,3,4,5];
let new_list = do_something(list);
lew newer_list = do_something_else(new_list);
Both these functions consume and return lists. In a strict language, this process of iterating over the entire list internally in these two functions would happen twice; this would be wasteful.A non-strict language however is permitted to take the side-effect-less code apart, and restructure it in such a way that the list is only traversed once, essentially combining these two functions into one function and letting what each does run successively, for this purpose it is permitted to generate a new function that combines what both does, and doing so iterating the list only once, and optimizers are typically clever enough to be able to do so.
Suppose that both of these functions be library function black boxes, provided by a dynamic library, then the implementation can no longer do this as it calls both from a library.
The way Haskell is written is completely dependent on this; it would be awfully slow if the compiler weren't allowed to do this.
The result in practice is that updating a dynamic library written in Haskell requires that all it's consumers be recompiled to compile against the new version. Or otherwise said: there is no concept of a stable a.b.i. in Haskell.
import System.IO.Unsafe
import Prelude hiding (last)
last :: [Int] -> Int
last (x:[]) = x
last (x:xs) = unsafePerformIO (do
print x
return (last xs))
secondToLast :: [Int] -> Int
secondToLast (x:y:[]) = x
secondToLast (x:xs) = unsafePerformIO (do
print x
return (secondToLast xs))
main = let
list = [1,2,3,4,5,6]
x = last list
y = secondToLast list
in print (x + y)
The above code outputs 1
2
3
4
5
1
2
3
4
11
indicating that it traversed the list twice, fully completing one traversal before starting the next. import System.IO.Unsafe
debug x = unsafePerformIO $ print x >> pure x
map1 = map $ debug . (+ 1)
map2 = map $ debug . (* 2)
main = let
list = [1,2,3,4,5,6]
newlist = map1 list
newerlist = map2 newlist
in print newerlist
The output of this is: 2
4
3
6
4
8
5
10
6
12
7
14
[4,6,8,10,12,14]
Showing that it was smart enough to traverse once.If `map1` and `map2` were imported by some library, it would be close to impossible to speak of maintaining stable a.b.i.'s to both these functions without the compiler somehow being able to turn them inside out and traverse only once.
This is due to the non-strict semantics.
Consider that in a C library, or any other strict language, the function loaded in the dynamic library is called by first evaluating all of the arguments, and then calling the function by value with the evaluated arguments.
This is not how Haskell operates, where the arguments are, in effect, placed inside the function body directly and may, or may not be evaluated, as such a function can't be a black box that can arbitrarily change it's internal workings, so long as the outside a.b.i. remain stable — a program compiled against a dynamic library is fundamentally compiled against the inner workings of functions, rather than simply against the stable interface they proffer to the outside world.
However, reading just the types of a well written Haskell API probably gives me greater inside in its usage than most documentation I’ve read on npm packages. (To name an example I’m familiar with)
There are so many very general patterns and idioms in Haskell being used all over the place that reading the signatures in the API docs can be extremely valuable.
(Haskell's direct links to source are also good, but there's no fundamental reason Python couldn't have those. I've never seen it though.)
Good. I set myself the challenge of compiling a Haskell program [1] during the Christmas holidays. It was meant to be a "one mince pie" challenge, but after an hour I discovered the VM I used didn't have enough RAM (during compilation we were approaching 4GB), then I ran out of disk space as stack approaches 5GB & I had other stuff installed. Once a few hours had gone by (this program isn't fast to compile) I had a working program. I now have to figure out if I can distribute just the resulting binary to other servers, or if it needs other software like GHC installing. Having finished the pack of mince pies, that can wait to another day.
I know when I first started compiling C/C++ software there was a learning curve and it took hours the first time, but I found it easier to get started. With Haskell, the way one version of GHC is installed first and then Stack installs a completely isolated version is confusing; plus the inscrutable error messages (haven't got it to hand, but one means OOM but doesn't say that - it takes a Google to find the GitHub issue to work that out).
And this is before I try and experiment/decide to learn some Haskell. Apart from the error messages they're not issues with Haskell per se, but they contribute to the experience of it.
gcc -Wall -O3 -o ./main main.c
ghc -Wall -O3 -o ./main main.hs
I still compile that way every now and then.Also relevant if you read through those comments, and the discussion they include on FP Complete's involvement, is Michael Snoyman's more recent statement: https://www.snoyman.com/blog/2020/12/haskell-foundation
I think Snoyman makes a few good points - namely, it remains to be seen exactly what the Foundation can and will do, and what input the community is going to have in that process. While the criticism that was once made of Facebook/IOHK/FPC applies here (namely, that a large enough force will trump Haskellers who invest in the community), the Foundation has all the power to be much worse. The academic side of the language still carries the most weight.
Because of that, I'm much more interested in how the Board will determine how it can best serve the community, and gather community feedback. That question can only really be answered after the board members themselves have been chosen.
> Salary will be commensurate with the experience, qualities, and location of the individual, and will also reflect the Foundation’s status as a non-profit organisation funded by donations.
"Call to order! We have here before us a motion to assign the task of deciding whether to remove 'Hello World' as a program from the documentation, given the ambiguity of its semantics. We have a second...the ayes have it, motion approved."
Or more like, "Here, let us nibble away at this $400k cheese, and by the time we get through the paperwork decide to make anything we'll hire someone on Fiverr to do a cornflower blue logo, but it will be SVG so they know we're serious.""
The complaint is that with lazyIO, even if your code does the write before closing the file, the actual order the operations occur in could change, to put the close first.
If you look at the lazyIO documentation, you will see:
>Although this module calls unsafeInterleaveIO for you, it cannot take the responsibility from you. Using this module is still as unsafe as calling unsafeInterleaveIO manually. Thus we recommend to wrap the lazy I/O monad into a custom newtype with a restricted set of operations which is considered safe for interleaving I/O actions.
https://hackage.haskell.org/package/lazyio-0.1.0.4/docs/Syst...
TLDR, don't use lazy IO.
I don't know which people you are talking about, but it clearly was in this specific subthread which started with a complaint about “LazyIO”, not “lazy IO”, and where the only use of “lazy IO” in a post before yours was in a post where all previous references were to “lazyIO” and it's specific package documentation, and contextually the “lazy IO” references was about the same thing, not something else.
One of Haskell's known weaknesses is that there are no curated catalogs of high quality libraries. The industrial strength stuff sits on package servers alongside the broken toys.
I once got an apology from a major luminary in Haskell, author of dozens of high quality packages, because I used a package he wrote that turned out to be an abandoned broken experiment, but not documented as such.
Yes, Haskell has those gotchas like "don't use lazy IO", "there are partial functions on the Prelude", "String is slow", and "code may throw exceptions where you don't expect".
They are there mostly because of history and the language would be better without them. Yet, they are easily avoided and aren't that many (those above are most of them). If you rage quit every toolset that has problems, I have some really bad news for you.
https://guide.aelve.com/haskell/alternative-preludes-zr69k1h...
This directly means "a problem with the lanuage" to any beginner with the language. Since it's the default Prelude. So a beginner has to somehow learn that there are issues with Prelude, that there are other preludes, find the differences between them, select one, and then reconcile the inevitable differences between those preludes and the official one when reding various docs.
...
And then figure out wich of the two dozen language extensions you need to use to even write something useful beyond a "Hello, world".
You're not doing Haskell any favors by comparing with "the times when STL was no good" (when was this? 20 years ago? 30?).
> Why should the situation with Haskell be different?
Because the world has moved on and expects languages, their libraries and runtimes to become better.
Huh, why not? He's comparing not equating, after all.
> And Prelude's problems are way less severe than the problems the STL used to have
For a language to be adopted it must be beginner friendly. And by beginner friendly, I don't mean easy. It can have a steep learning curve as long as it enables the user to be productive, otherwise the beginner will give up quickly.
For the experienced user things like, "don't use lazy IO", "there are partial functions on the Prelude", "String is slow", and "code may throw exceptions where you don't expect", may be obvious, but are not for the beginner. I don't remember exactly how I ended up using "lazy IO", but trust me it wasn't because I ignored a big fat warning that I should NOT be using it.
It's gotchas like these that prevent the beginner from being productive.
Then the beginner reaches out to the community sharing his experience and gets a defensive and passive aggressive reply like yours. It's enough to make them not want to touch that language ever again.
You think you are defending your favorite language, but actually you are doing more harm.
once you are the expert, you would waste your time to use other programming languages.
also - the post you are replying to isn't condescending. the haskell community just doesn't treat beginners like infants who need coddling. haskellers tend to give the nuanced & truthful answer to questions.
Expert in what? Programming? Lambda calculus? Category theory? Let’s not pretend that you are simply an expert in something randomly, it takes a lot of practice, mistakes, and actually being a beginner before being an expert. Even if you are an expert in the above you still are a beginner when learning Haskell the first time, and you will make mistakes, so having beginner friendly resources is important.
Quite frankly, it’s this kind of gatekeeping attitude - that Haskell is meant for “us” and to become one of us, you have to be an expert, and we aren’t going to coddle you - is a kind of attitude that significantly contributes to the lack of good and welcoming beginner resources and causes very few people to actually pick up and stick with the language. You can be a “language for experts” and still have good, beginner friendly learning resources to welcome more into some community.
There are plenty of beginner-friendly resources already (more & more every year), but they can't erase worse ones from the Internet. The Haskell community lacks the sort of strong-fisted leadership to accomplish that by design.
That said, acting like a tutorial mentioning lazy IO or partial head/tail is a killer is a little ridiculous. Neither of those things are atrociously problematic, so learning about them early is fine. You may get cut due to them, but getting cut is okay if you don't flip the table.
Also I (and most of Haskell posters) do not gatekeep. The community will go well out of its way to respond to any questions beginners have with blog-post-quality comments in the various forums (mailing list, reddit, irc, slack, github issues, etc) There is definitely a teaching culture, which is the opposite of gatekeeping.
We could special-case it, but on the other hand, there are already multiple other solutions in this space that are more generally useful. Alternate Preludes (and compiler support of -XNoImplicitPrelude) `hlint` can also be customized to do exactly what you're asking for as well.
That's going to make it very hard for me to run a business, which involves sourcing, hiring, and training a potentially large number of people. Hiring only experts is a tremendous business cost.
Haskell is first and foremost a gigantic personal productivity booster. It allows me to do bigger and more complex projects on my own or with one or two others. These projects are not for corporate interests although that can be for personal profit. I am pretty confident I will be able to stop giving my labor to any corporation within the 10 year mark of my career, and Haskell will be a big reason why.
But I still try to convince corporations to use Haskell since then we offload the cost of learning onto a corporation instead of an individual. That feels like a worthwhile re-allocation of capital to me! Corporate Haskellers get paid to gain expertise and keep it forever, leaving corporation with nothing besides their direct labor. Luckily it's easy to convince a corporation of anything with what amounts to propaganda & politics.
How very modest of you, if only they could listen.
That said, none of that applies to those gotchas. They are clear problems with the language. None of them are obvious, but all of them are widely discussed so you will see them early when learning the language. Learning the gotchas of an ecosystem is a central part of learning any tool on informatics, and as gotchas go, one of the strong points of Haskell is that there are very little of them.
You are making a very common complaint, that comes from ignoring the problems of the tools you know while making a fuzz over anything you find on the new one. It's almost certain that whatever languages you are used to, they have a much larger pile of gotchas than Haskell (because Haskell has an atypical small number of them), but that doesn't stop them from being useful.
Some of the "C++ contenders" (besides Rust) like Nim, Zig, and potentially Swift seem to balance this line well: simple to start using, but grow with one's expertise.
"Yeah well your language isn't good either" isn't a very good argument for why a language is _better_. If the idea is "Haskell works better than other languages as long as you happen to know how to make it so", isn't that just every language that exists? Due to Haskell's branding, I was under the impression that the issues the above user is describing should be impossible. If Haskell doesn't even make good on those guarantees, but instead shifts that guarantee as "Things you should've known not to do" onto the user, how is that different from any other language?
edit And since this thread has a lot of tension and vague hostility, I will add, I am not attacking you or the language you enjoy. I legitimately would like to better understand.
" < language > is only safe if the authors of such code take advantage of the features that the language provides to write safe code "
But "you won't be able to use it well if you don't learn how to use it" does indeed apply to all languages. Yet, it's more relevant for some, and Haskell is one of those more affected by it.
It is a fact that Haskell is not beginner friendly. Your first program on it will suck, whatever proficiency you have with other languages (there are some people that argue that it is easier if you know less beforehand). It takes some learning before you are able to get the better safety, high productivity and easy collaboration people talk so much about (and even then, you won't get those for every problem, the language has limitations too).
1 - The statement is false anyway. There's no such language one can single out there without more context.
As someone who has tried to learn Haskell twice and failed (and trust me I went way beyond the second page of a tutorial ;-), I don't think the issue is that Haskell has problems. As you hinted every language has problems. The issue is that for a lot of people Haskell requires significantly more effort to learn than other languages and doesn't have a lot publicly available software to show off for. So when we learn Haskell and stumble on problems, we are less forgiving than with other languages because we have spent more effort and we don't have enough examples of useful software made with Haskell to reassure us that our efforts will eventually pay off.
We loose confidence because it seems that the promises of correctness aren't fulfilled and we don't have enough evidence that we aren't wasting our time. We would need at least to see the light at the end of the tunnel to keep us motivated. The sad thing is that if we decided to spend long hours of our free time learning this language instead of doing other things it's because we really believe in ideas behind it. What we have read about the language obviously speak to us but at some point we loose motivation because we don't get any tangible results.
I'm pretty sure that a good part of why it clicks for some people and not others is due to the amount of mathematical literacy. I don't have a strong mathematical background so the amount of effort for me to learn the language is probably much higher than it is for someone better at math. I have to invest more so I expect more in return, which in my personal experience Haskell fails to deliver. To appeal to a wider audience Haskell needs to be pretty much flawless and also to have more public software to showcase.
Sometimes you go into the tunnels that are not tunnels but rather mines, and you go there not to see the light at the end of it, but rather to dig diamonds.
There are other strange things in the IO monad, such as the ability to observe evaluation orders by throwing exceptions.
That's not specific to IO; you can more or less always observe evaluation order by throwing exceptions:
bar = error "bar"
baz = error "baz"
foo = (bar,baz) :: (Int,Int)
main = print foo -- well, I guess you need IO to run anything at all
`bar` is evaluated first (at least on my machine), as evidenced by: $ ./test
test: bar
CallStack (from HasCallStack):
error, called at test.hs:1:7 in main:MainIts a bit more that that. You need to be in IO in order to catch an exception.
That is to say, if you have:
bam :: (Int, Int) -> String
There is no way to write: bam (x,y) = "bar" if x is called first
"baz" if y is called first.
However, if you were to instead have: bam :: (Int,Int) -> IO String
Such a function is possible to write, as you can now catch the exception and do something with it.In this case the Haskell program itself does not observe the order, as it crashes, but the order is printed as debug inormation.
Lazy IO, like the partial functions in Prelude, is a convenience hack for making some very low-complexity things easy. I don't have a problem with it existing, because I am fine with matching my tool complexity with the problem complexity.
And also, I understand what it does. I'm surprised I'm the first person to point out that your assertion is not an accurate description of anything that actually happens.
The promises aren't “using Haskell magically causes correctness”, but “Haskell provides powerful tools that most other languages don't that enable correctness”.
But, you know, the people that need to telegraph “rage quitting” a language are usually the people chasing an unreasonable silver bullet and misinterpreting things through that lens to start with.
I just don't think you can make an honest business case for it on a greenfield project. That's my opinion, is there a chance I'm wrong?
Which source materials did you use? What is your background? How comfortable are you with higher level typing systems?
I have very little hands-on experience, but with the little I messed with both languages, they serve different purposes and are not apples to apples comparison.
- If you're working with complex domain objects, Haskell has somewhat better (edit: or at least more powerful) tools for forbidding invalid states during runtime and assisting refactors as your data model changes.
- Haskell seems to have better libraries for writing parsers and compilers than Rust, if that's relevant to your project.
- Some folks prefer ML syntax to Algol-like syntax.
- The GC, immutable-by-default data, and reasonable concurrency model are pretty nice features of the GHC runtime.
- The performance difference between Haskell and Rust probably doesn't matter for most projects.
- Haskell might actually compile faster if you avoid crazy type-level programming shenanigans.
- Haskell has a pretty decent REPL.
Obviously, Rust has a different set of trade-offs, and the bigger cost to either is going to be that both are a bit off the beaten path and require some practice and experience to be productive and comfortable in.
Rust is a nice modern language that's both imperative and functional with great tooling and libraries.
What makes Haskell better in this area in your opinion?
But I agree - why do people seem to go out of their way to hamper themselves with pun mottos that are just begging to be misunderstood, either genuinely, or deliberately and used against them?
Many people don't stick around for the 'ah it actually means...' bit where you unveil your witticism. They just go away with the face-value explanation and don't bother to learn more.
Like 'free software'. 90% of people aren't sticking around to hear the pitch about 'free as in freedom'. You've already missed your opportunity to explain your point of view - you wasted it on making a pun instead of actually communicating! It's madness.
Edit: I think I read this explanation: https://news.ycombinator.com/item?id=12056169#:~:text=haskel.... "Haskell would prefer to be powerful, safe, efficient, obscure and niche; rather than popular, industry-standard, widely-known, highly compatible, unsafe, insecure, inefficient and restricted"
Not an actual quote, but I liked the scene it brought to mind.
You don’t have to use Haskell directly to benefit from it.
Saying that it’s a shibboleth—well, that’s kind of a crass way to dismiss an entire community. It’s rude and not insightful.
Of course, the quality of a language is at best weakly correlated, and at worst anticorrelated, with the amount of useful software that is written using it.
Your comment was dismissive, and I’m pointing it out because if nobody responds to people making rude comments, other people will think it’s okay to make comments like yours.
I think the “secondary” value it has created is immense. I’m not a Haskell programmer by any stretch, but thinking in terms of concepts evangelized by Haskell (isolating side effects, first class functions, monads) has totally changed the way many people design systems in conventional programming languages.
So, this may be a learning opportunity for me, but I thought the idea of separating out side-effects at the process boundary is a very old idea. The same for "first class functions". Haskell was invented around 1985. Lisp was 1958, Smalltalk 1972.
Besides, isn't Haskell fundamentally about about lazy evaluation and rigorous type safety?
I’d typed a whole paragraph on LISP, but deleted it because I’ve seen that whole regurgitation of history play out so many times on HN. :)
My comment was looking at Haskell as the standard bearer for FP, but it’s not of course.
I agree that lazy evaluation and rigorous type safety are what Haskell is best known for nowadays, especially since its less controversial components have found their way into most conventional programming languages by now.
If you were using Lisp or ML, you could write pure code in the core of your program but fall back to impure code wherever you liked, because purity was not ever enforced. This explains why nobody really used stuff like monads in Lisp or ML—it wasn’t practically useful, because you could always just write impure code.
> Besides, isn't Haskell fundamentally about about lazy evaluation and rigorous type safety?
I’d say—no, absolutely not. It’s not about either of those things.
“Rigorous type safety” is kind of an ill-defined concept. I’d say Java has rigorous type safety, but it’s very different from Haskell. The lessons from Haskell is that function purity (side effect isolation) is incredibly useful, and laziness is the historical reason why functions in Haskell had to be annotated with the correct types—because a "print :: String -> ()" would not work in Haskell.
In other words, the fact that Haskell uses lazy evaluation forced the development of type system capable of expressing side effects and sequential operations, and now that we’ve discovered how to do that, we could discard the lazy part and make an eager version of Haskell. Some people have done that, there are a couple eager variants of Haskell out there. They are very recognizably Haskell.
There are a couple other cool things that came out of Haskell’s purity, like software transactional memory. Haskell didn’t invent STM, just like it didn’t invent purity, but Haskell is still the one major success story for STM. The lesson from trying to port STM to other languages is that Haskell’s purity is what made it successful there. Lazy evaluation, in a sense, is just the thing that breaks your program if your functions are not pure, keeping you honest.
For...what group? I mean, any language can be trivially seen as a shibboleth for the language community, but that's not worth falling our for any language specially over others.
"I'm convinced that if only we could get a word in with management, and explain what [buzzword] is, they'd consider using [technology]"
I get an error 'xcodebuild requires xcode' and 'C compiler cannot create executables' when running `stack setup`. I have the Xcode Command Line Tools installed. Anyone had similar experience?