Frequently asked questions

Got a question? You can ask in the book's issue tracker!

General questions

How to install editor tools

As far as I know, the most recommended setup today for Haskell development is using VSCode or VSCodium together with the marketplace Haskell extension.

The Haskell extension uses haskell-language-server which can be install via GHCup or even via the Haskell extension itself.

There are other options of course if this setup isn't your jam.

How to learn new things

The Haskell community keeps marching forward, developing new libraries, tools and techniques as well as creating new material for older concepts. The Haskell planetarium aggregates feeds from several communities into one page, as well as a Haskell Weekly newsletter. You might also find the quite a bit of Haskell presence on Twitter!

Debugging

How to debug Haskell code

Most imperative languages provide a step debugger. While the GHCi debugger, exists it is not particularly easy to use, especially because of Haskell's lazy evaluation where things might not evaluated at the order we might intuitively expect. Because of that, Haskellers tend to use trace debugging and equational reasoning. With trace debugging, we try to verify our assumptions about the code - we use the various trace functions as a "hack" to print variables, functions inputs, functions output or even just say "got here", from anywhere at the code.

After finding something that does not match our assumptions, such as unexpected input or output of a function, we try to think what piece of code could be responsible for the discrepancy, or even use trace debugging again to pinpoint the exact location, and try to use "equational reasoning" to evaluate the offending code that betrayed our expectations. If it's easy to do, we try running the function in ghci with different inputs to check our assumptions as well.

Because Haskell focuses on immutability, composibility and using types to eliminate many classes of possible errors, "local reasoning" becomes possible, and trace debugging becomes a viable strategy for debugging Haskell programs.

How to understand type errors

GHC type errors are often not the most friendly errors messages, but they mean well! They are just trying to help us find inconsistencies in our code - often with regards to type usage, they help us avoid making errors.

When you run into error messages, start by reading the messages themselves carefully until you get used to them, and then the offending code hinted by the error message. As you gain experience, it is likely that the most important part of an error will be the location of the offending code, and by reading the code we can find the error without the actual error message.

Adding type signatures and annotations to test your understanding of the types also helps greatly. We can even ask GHC for the expected type in a certain place by using typed holes.

My program is slow. Why?

There could be various reasons. From using inefficient algorithms or using an unsuited data structure for the task in terms of time complexity of the common operations, to less efficient memory representations (this is another reminder to use Text over String in most cases), and laziness issues (again, the evaluation strategy!).

The performance section in my Haskell study plan links to various resources on Haskell evaluation, profiling and case studies.

Design

How to structure programs

Start with the imperative shell functional core approach, define EDSLs with the combinator pattern for logic if needed, use monadic capabilities such as State locally if needed, maybe add an environment configuration with ReaderT, see how it goes.

If that approach fails you, look at why it failed and examine other solutions according to your needs.

How to model data

Modeling data using ADTs are usually the way to go. Often programmers coming from object oriented background tend to look at type classes as a way to define methods similar to inheritance, but this often isn't the right approach and ADTs with different constructors for different alternatives go a long way. Remember that even OOP people often preach for composition over inheritance.

Use functions to define behavior on data rather than trying to couple the two together.