Frequently asked questions
Got a question? You can ask in the discussion board or the issue tracker!
General questions
Why should I learn Haskell
I've written a couple of articles on the topic:
- Consider Haskell (Alternative title, 'What can I do with Haskell?')
- 7 things I learned from Haskell
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 installed via GHCup or even via the Haskell extension itself.
If you already have a preferred editor, see if HLS supports it, or alternatively use GHCid which provides rapid feedback independently from an editor.
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 quite a bit of Haskell presence on the Fediverse!
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 be evaluated in 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 in 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, composability, 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 error messages, but they mean well! They are just trying to help us find inconsistencies in our code - often with regard to type usage, they help us avoid making errors.
When you run into error messages, start by reading them carefully until you get used to them, and then the offending code hinted at 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 inefficient algorithms or
unsuited data structures 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 capabilities such as State
locally if needed,
maybe add an environment configuration with ReaderT
, and see how it goes.
If that approach fails you, look at why it fails 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.