Debunking Haskell Myths and Stereotypes

zelenya

Zelenya

Posted on March 23, 2023

Debunking Haskell Myths and Stereotypes

Haskell is covered with myths and stereotypes, such as “You need a Ph.D. to do Haskell” or “Haskell is only good for writing compilers”. These are silly and not true.

Let’s debunk the most common ones.


📹 Hate reading articles? Check out the complementary video, which covers the same content.


I want to cover the four main categories of stereotypes, which can be expanded into another set of myths:

  • Haskell is only for academics.
  • You can’t do anything “useful” cause there are no side-effects.
  • The Haskell tooling is very bad.
  • Haskell has an ivory-tower community.

Haskell is only for academics

Yes, academics use Haskell, and it’s very nice for them. No, you don’t need a Ph.D. to use Haskell.

It’s like saying that Go is only for Google (Alphabet) developers.

Academia

This meme has two sides:

  • Haskell is only for academic Haskell – not real-world applications.
  • Haskell is too complex and challenging to learn.

First, Haskell is used in many production systems: companies such as Facebook, Verizon, Nasa, etc.


💡 Check out this list with some of the companies that use Haskell in production.

I’ve personally worked with three companies on this list. So, it’s possible.


Second, Haskell can be hard to learn. But programming is hard. Learning programming for the first time is hard.

If you have years of experience, you’ve probably picked up multiple languages. And when you try to learn a new language, such as Go, Rust, or Kotlin, it feels familiar; of course, there are some new and different ideas, but you’re not starting from scratch.

When you try Haskell, it can feel very alien – there is a bunch of new things all at once. Haskell's syntax and concepts can take some time to understand, but once you do, you will have a whole new, powerful, and expressive toolbox at your disposal.

And then picking up PureScript, Elm, or whatever will be easier too.

One thing worth pointing out: in some cases, tooling and errors aren’t beginner-friendly, at all. Which requires additional learning and perseverance.

You need to know category theory

There are a lot of Haskell tutorials that explain things in terms of category theory (CT), which can give a wrong impression that category theory is fundamental for Haskell.

Especially when people bring up the category of ✨ Hask ✨.

category theory

Well, compiler (GHC) developers don’t consult with category theory when developing, most of the library developers too, and especially regular developers.


💡 GHC is the main Haskell compiler.


“To understand is to perceive patterns” (Isaiah Berlin)

Category theory is quite abstract, but also it’s pretty neat and exciting. CT raises many interesting questions (discussions); for some, it can be a helpful framework for thinking about software, logic, and the relationship between different objects. For others, it’s useless and overcomplicated.

But most of us mortals get into the categorical trap sooner or later.

So, do you need to understand category theory to write Haskell code or use libraries? No.

Is category theory useful? Yes.

Is it nice that there are intelligent people who understand category theory and write valuable libraries, so I don’t have to? YES!

You have to learn about monads as soon as possible

This is more of a legend than a myth. I’ll keep it short.

Monad is yet another pattern. There is nothing magical about it.

Somehow nobody ever says:

  • “I want to learn Java; what is a Visitor or Abstract factory?”
  • “I want to learn Ruby; what’s MVC?”

After you learn and use the language for a bit, you can start recognizing and using patterns.

Yes. You can use Haskell without knowing Monads. You may have to relearn the same things when you use different language constructs. But this is what people do in every programming language.

  • Learn the basics of the language.
  • Learn how to use lists, optional values, async/await, etc.

And then, in the case of Haskell, eventually see the patterns. And when it clicks, you can use the same tools (and syntax) to solve various problems: old and new.

-- Loop over two collections
nestedLoop :: [Text] -> [Int] -> [Text]
nestedLoop services operations = do
    service <- services
    operation <- operations
    pure ("Service " <> service <> " does " <> toString operation)
Enter fullscreen mode Exit fullscreen mode
-- Get two optional values
getCached :: HashMap String Double -> Maybe Double
getCached cache = do
    result_a <- HashMap.lookup "Service A" cache   -- early return if Nothing
    result_2 <- HashMap.lookup "Operation 2" cache -- early return if Nothing
    pure (result_a + result_2)
Enter fullscreen mode Exit fullscreen mode
-- Wait till the service is ready and then do the work
prepareAndWork :: IO ()
prepareAndWork = do
    service <- prepareService
    doWork service

data Service = Service deriving (Show)

prepareService :: IO Service
prepareService = do
    print "Preparing a service"
    pure Service

doWork :: Service -> IO ()
doWork service =
    print (show service <> " is working")
Enter fullscreen mode Exit fullscreen mode

Or, in the case of other languages, use different constructs for different things.

// Loop over two collections
fn nested_loop(services: Vec<&str>, operations: Vec<i32>) -> Vec<String> {
    let mut result = vec![];

    for service in &services {
        for operation in &operations {
            result.push(format!("Service {service} does {operation}"))
        }
    }

    result
}
Enter fullscreen mode Exit fullscreen mode
// Get two optional values
fn get_cached(cache: HashMap<&str, f64>) -> Option<f64> {
    let result_a = cache.get("Service A")?;   // early return if None
    let result_2 = cache.get("Operation 2")?; // early return if None
    Some(result_a + result_2)
}
Enter fullscreen mode Exit fullscreen mode
// Wait till the service is ready and then do the work
async fn prepare_and_work() {
    let service = prepare_service().await;
    do_work(service).await;
}

#[derive(Debug)]
struct Service;

async fn prepare_service() -> Service {
    println!("Preparing a service");
    Service
}

async fn do_work(service: Service) {
    println!("{:?} is working", service)
}
Enter fullscreen mode Exit fullscreen mode

You can’t do anything “useful” cause there are no side-effects

This is a widespread misconception.

Some tutorials make it seem like Haskell is quite limited: it can’t interact with the outside world or be used with anything that has a state; for instance, we can’t even fetch data from another service.

Referential transparency

This isn’t true. But it does turn people away from Haskell without even trying it.

Here is an example of the program that reads from the standard input and prints into the standard output:

module Main where

main :: IO ()
main = do
  putStrLn "What's your name?"
  name <- getLine
  putStrLn (name ++ " has just interacted with a Haskell program.")
Enter fullscreen mode Exit fullscreen mode

If we run it and input the text Jay, it prints Jay has just interacted with a Haskell program.

I hope this shows that the myth is a myth; it's not true.

The thing is that “pure” functional programmers use a different definition of the term “side-effects”. That’s why saying “There are no side-effects in Haskell” doesn’t mean that Haskell can’t do any input/output or interact with external programs. 

Long story short, there is a difference between evaluating and reasoning about the code without side-effects and executing this code, which has side-effects.


💡 If you want a proper explanation, check out Referential Transparency. I have a separate video and article which covers this topic.


Haskell isn’t suitable for web development

Building on the previous misconception, Haskell is sometimes perceived as unsuitable for web development. Somehow Haskell is perfect for writing compilers but nothing else.

Well, it’s perfect for writing compilers, but whatever makes it perfect for this task makes it great for others. Haskell features and libraries allow us to write more maintainable and robust code and eliminate a large variety of errors at compile time.

For example, Haskell encourages modeling the business domain and making all the assumptions explicit.

On top of that, Haskell has several frameworks and libraries for building high-performance and scalable web applications. For example,

  • IHP batteries-included web framework;
  • Yesod web framework;
  • Servant library, which allows representing a web API as a Haskell type (which means you can check if your server, client, or both implementations respect the API specification at compile time).

And there are around a dozen mature Postgres libraries, all levels of abstractions: from low-level queries to the type level. So, you’re covered.

And it’s not just web development; Haskell is a general-purpose language – you can do anything.

Maybe not embedded or systems programming. You should prefer using something like Rust or zig in this area.


💡 If you want a detailed overview of the Haskell ecosystem, check out the State of the Haskell ecosystem by Gabriella Gonzalez.


There are no libraries

Regarding the libraries’ availability, some domains are less mature than others. But it’s not that dramatic. Yes, if you ask a Haskell developer if you should only use Haskell for your production Machine Learning pipeline, they will tell you no. But it doesn’t mean that you can’t do it at all.


💡 The same State of the Haskell ecosystem showcases some available libraries per domain.


In this case, there are Haskell libraries, as well as bridges and integration for other ecosystems. So it’s doable depending on the task.

Chicken and egg

Haskell has a smaller ecosystem than mainstream languages; therefore, there are fewer libraries and tools available, and you might need to write more code from scratch.

It hurts the most when it comes to integration with other teams. While they can just plug-and-play any fancy technology (more or less out of the box), we have to write from scratch or tweak existing libraries to serve our purposes.

The Haskell tooling is very bad

There is this idea that there exists the “Tooling issue” (with a capital T) in Haskell.

Maybe it was true back in the day. But it’s not true anymore. Long-story short: you install one tool, GHCup, and one editor and have all the autocompletes, auto-imports, goto-definitions, etc. On top of that, Haskell has some tooling that other language ecosystems can only dream about.

And, of course, there are some issues here and there; we’re all software engineers, and we know how it is.


💡 If you’re a curious, I’ve also covered this topic separately in Debunking Haskell Myth: “Tooling Issue”.


Performance is very bad / Haskell is slow

Haskell is often perceived as being slow compared to other programming languages, but this is not generally true – Haskell's performance can be on par with other programming languages and even faster in some cases.

GHC generates optimized, native executables. The runtime system supports green threads. Haskell is an excellent choice for high-load concurrent applications like web backends.

I’ve primarily worked in web development, so I’m the last person you should ask about “real” high performance. But some people care about performance – they share their experience and actual numbers.


💡 For example, here is a recent Reddit discussion.


On a related note, is Haskell compilation slow? Yes, because it's doing the work for us. I don't mind waiting a few extra seconds instead of spending hours debugging some runtime errors. Like other compilers, Rust, for instance: nothing comes for free.

It's compiling

Also, a quick reminder: you don’t have to “fight the compiler”; a compiler is your friend and is here to help.

Toxic / ivory-tower community

Not gonna lie, unfortunately, this is somewhat true.

Community

You don’t have too look too hard to bump into a rude opinionated person who is happy to tell you how wrong you are. For instance, if you imply that some learning method doesn’t work for you, at least one person will happily point out that you are wrong about yourself (and your feelings).

But it doesn’t mean the whole community is like that, it’s just people like that are often the loudest.

If you are lost and have a question, you will get an answer – there are a lot of nice and welcoming people in the community.


😊 Check out what Simon Peyton Jones loves about the Haskell community.

“I love the quirkiness that Haskell seems to inspire, a combination of playfulness and deep intellectual insights” (Simon Peyton Jones)

“I love the individuals of the Haskell community. You are so smart, so motivated, so hard working. Through your efforts, Haskell has gone from an academic project with a not-so-great compiler to an industial-strength ecosystem with a huge collection of libraries and tools” (Simon Peyton Jones)


The problem is that you have to either learn to filter these other people (and comments) out, or take some time to find a smaller and nicer sub-community. For example, you can find a cozy Discord server.

So, yeah, if you want to hear that you’re “well, technically” wrong on some topic, you’re welcome.


Tune in next time when I complain about people who complain about syntax.

💖 💪 🙅 🚩
zelenya
Zelenya

Posted on March 23, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related