Haskell Safety

Rebecca Skinner on 2022-06-30

Photo by Joe Dudeck on Unsplash

A Quick Take on Haskell’s Strengths and Challenges

https://pragprog.com/newsletter/

It’s really interesting to me how often I hear people talk about Haskell as a very safe language. The truth is that I don’t think Haskell is actually a particularly safe language in a lot of ways. If you put together a list of the least safe features you can have in a language: null values, raw memory access, type coercion, runtime exceptions — in theory we can do all of that in Haskell. In fact, there are even a few areas where Haskell is uniquely innovative in the arena of things that can go wrong when we start to introduce lazy evaluation and look at some of the unexpected bugs that can come out of things like lazy IO.

That’s not to say that Haskell programs are prone to bugs and errors. Haskell gives you the option of unsafe features when you need them, but out of the box it also does a lot to encourage programs that run correctly. First, as a pure functional language, Haskell removes the risk of bugs caused by global mutable state. Second, Haskell is garbage collected and doesn’t require us to manually deal with memory, saving us from quite a few common sources of errors. Third, Haskell’s strong static type system not only gives us a lot of power to create our own safety, but out of the box it buys us a lot of assurances that we haven’t done totally wrong things or made some obvious mistakes.

Ultimately, Haskell has always seemed like a deeply pragmatic language to me. You get all of the tools necessary to build extremely safe programs that can’t go wrong, but the cupboard full of double-edged swords and footguns is always there in the corner if you need it. It’s up to you as the individual developer to understand the tradeoffs and figure out how much safety you want — or want to give up — in your particular application.

Dial in Your Safety Preferences

The choices Haskell gives you aren’t just about how safe you want your program to be, you also get to choose where you want to build in the safety. In most languages, building safe code comes down to defensive practices: writing lots of tests, validating your inputs, and carefully hiding the implementation details of your application behind simplified interfaces.

We do all of those things in Haskell too, but the combination of pure functional programming and the expressiveness of the type system give us techniques for designing code that is resilient to misuse at compile time. In effect, writing safe Haskell code ends up being the process of writing very small informal DSLs (domain-specific languages) that allow the user to write only working code in the first place.

Safety in the Ecosystem

Haskell gives you a lot of flexibility in how you add safety to the code you write, but a program is only as safe as the ecosystem around it. This is another place where Haskell tends to give you a little bit of choice. A good example of this is in the Haskell standard library. For various reasons, Haskell’s standard library includes quite a few commonly-used functions that are actually pretty unsafe. For example, the head function that gets the first item from a list. The version of this function that’s part of Haskell’s standard library will fail with a runtime exception if you call it with an empty list. This is obviously a pretty glaring omission if a language wants to be safe above all else, but if we’re willing to compromise from time to time, there are some advantages to unsafe functions like this too.

In the case of head and some of the other unsafe functions in the standard library, this lack of safety buys us backwards compatibility both with older programs and with older documentation that teaches people Haskell using the original unsafe definitions of these functions. Even for new texts, like Effective Haskell, these unsafe functions are useful because they can let learners focus on the underlying concept of how to do things like work with lists without having to deal with the edge cases that can detract from learning.

These unsafe functions in the standard library are all well and good for programs that don’t need a lot of safety, but for production applications where we want to eliminate as many errors as possible, we have other options. The Haskell community has created several alternatives to the standard library that provide safer versions of common functions that eliminate the risk of runtime errors. This same pattern holds with other parts of the Haskell ecosystem, where you’ll find different libraries offer different tradeoffs in terms of safety, complexity, and ease of use.

Be sure to check out Rebecca Skinner’s book from The Pragmatic Bookshelf, now in beta. You’ll find a code that saves you 35% on the book’s forum page on DevTalk, where you can also contribute to the conversation about the beta. Promo codes are not valid on prior purchases.

Effective Haskell Put the power of Haskell to work in your programs, learning from an engineer who uses Haskell daily to get practical…pragprog.com

https://pragprog.com/titles/rshaskell/effective-haskell/