Skip to content

Instantly share code, notes, and snippets.

@infinity0
Last active February 22, 2021 13:57
Show Gist options
  • Save infinity0/cd5894659e28c7bf8b6dee4d9abfa55e to your computer and use it in GitHub Desktop.
Save infinity0/cd5894659e28c7bf8b6dee4d9abfa55e to your computer and use it in GitHub Desktop.

Haskell

This is a quick "zero to hero" guide of various Haskell and FP-related topics. The intended audience (you) is self-directed people that want to quickly become a world-class Haskell programmer. Actually many of the topics are not specific to Haskell, so it can probably also help you become a world-class programmer in a similar language too.

The format is intentionally terse - most of the content does not even consist of full sentences. This is not just because I am lazy - rather, it's 2020 and we have the internet, so there are plenty of very good resources out there for going in deep into any particular topic, at least in computer science. However if you're completely fresh starting out in some topic, it's really not obvious what you should look at first. Often you type something into Your Favourite Search Engine, find some cool looking stuff, then have to spend a few weeks doing depth-first-search or breadth-first-search over all the jargon you come across, that is necessary background to understand the world-class texts.

These documents are intended to make this much quicker, by telling you a good order to explore topics in, starting from the basic ones. It is by no means complete, nor objective - it presents my/our views of a good "trunk" of progress, and omits side branches that you are supposed to figure out for yourself. If you get stuck or can't find inspiration on how to explore what's out there, simply do a web or video search for "$phrase tutorial".

Even with the theory topics, you should write some code (or if unavailable, do some practical exercises on paper) that implements whatever idea it's talking about. I and many others find this to be much more effective at really understanding something, than merely reading about it.

https://byorgey.wordpress.com/2009/01/12/abstraction-intuition-and-the-monad-tutorial-fallacy/

Struggle is somewhat necessary.

Initial reading

Introduction tutorials

Base topics

  • Basic syntax and functional style
  • Basic type system, data types and polymorphism

Basic typeclasses

  • Functor, Applicative, Monad - in pictures
  • Monad combinators (forM, when, etc)
  • Foldable, Traversable
  • Monoid, Semigroup

Practise

General tips

Build system

It's 2020, just use cabal "new-style" commands. We don't need stack any more.

Browsing and searching source code

grep/rg, these are more flexible than any IDE, learning to use them will save you tons of time

Hoogle, useful for when you remember a type signature but forgot the name

I don't need/use a fancy IDE, just a simple one (Geany) that lets me (1) "jump to definition" (ctrl-click) and (2) code folding.

Playing with your own code

Type Holes are very useful for debugging or building up a new complex thing from scratch.

Watch out for accidental recursion. Haskell is slightly annoying in that recursion is implicit so if you accidentally write something like:

let thing1 = ... innocent ...
let thing1 = ... thing1 ...

this will cause a runtime infinite loop that sometimes is confusing to debug. (In OCaml and others, the last thing1 refers to the first thing1.)

Higher-kinded functions (combinators)

Get familiar with this, it is not time-efficient to skip this part.

Type signatures using partially-applied functions

  • using functions as arguments
  • returning functions

Currying, especially curried/uncurried functions given as input to combinators.

Implementing algorithms as recursion instead of iteration

Get familiar with this, it is not time-efficient to skip this part.

How to mechanically - i.e. without really thinking about it - convert iterative algorithm to functional/recursive, e.g. using accumulators when needed. Try doing it manually and intuitively first, then read about the general technique which is actually very mechanical: defunctionalise the continuation (more detailed)

foldl/foldr, space leaks

Y fixed-point combinator and some common usages (e.g. memoisation). But don't spend too long on this, recursion schemes (much later) covers this more deeply.

"Real world" code

Common type classes

Eq, Ord, Show, Read, Enum, Bounded

Generic

deriving

Common data structures

(cabal packages): bytestring, containers, unordered-containers

Using monad transformers

Get familiar with the standard monad transformers (ReaderT, WriterT, StateT) and MonadTrans/lift.

Slightly more advanced: MaybeT, ExceptT; ignore ContT for now

Bad design pattern: a monad transformer with more than copy of a standard transformer, e.g. "ReaderT env1 (ReaderT env2 IO)". Instead write your own application-specific monad, perhaps wrapping standard ones inside a newtype. Don't build a monad transformer stack too high, basically.

Memory usage & fixing space leaks

seq and $!, BangPatterns, strict field annotations

GHC RTS profiling, ghc-heap-view

strict data structures, problem with lazy instances of common data structures e.g. haskellari/strict#22

TL;DR: use strict fields for long-running ("state-like") state, lazy (default) data types for "control-like" state. This is much more convenient / intuitive than the standard advice to sprinkle seq at random places in your code.

Quickly fixing type errors

Understand that typeclass constraints are solved using information available on the caller's side, it helps to think of them as function inputs (which is actually what they are desugared into).

Get experience in debugging monad transformer errors, which situations are fixable or impossible to fix.

System utilities

IO, ST monad, files, network, etc

Concurrent programming

STM, queues

Threads, Async, exceptions, blocking operations, interruptibility, masking Control.Exception.Safe, MonadUnliftIO and their caveats

Advanced theory and practise

Much "real-world" Haskell (e.g. today's production systems) only use techniques up to the end of the previous chapter then abuse the ReaderT design pattern which is just global variables implemented in Haskell - convenient but misses the point and power of doing stuff in Haskell in the first place.

It is possible to be very productive with this pattern, but IMO this is basically writing non-Haskell impure code in Haskell. To take advantage of Haskell's full power to express well-structured composable pure code, one should avoid this pattern and use the techniques below.

Existential types and GADTs

ScopedTypeVariables

Pattern matching to expose an existential type. Continuation Passing Style "withSomeX" design pattern to make this reusable.

Phantom types, Proxy vs TypeApplications, AllowAmbiguousTypes

Type families

https://wiki.haskell.org/GHC/Type_families

https://mmhaskell.com/blog/2019/2/4/why-haskell-v-type-families

Associated type synonym is similar to "associated types" in Rust/Scala

Advanced typeclasses

  • Alternative
  • Selective
  • Bifunctor, Bitraversable
  • Category
  • Arrow (Arrow notation, combinators and transformers)
  • Profunctor

Lenses

Lens over tea

Profunctor optics,

Performance-oriented features

Unboxed types

FFI

Transcendental

The below is not really necessary for most programming tasks, but it's useful info for those that want some deeper level of understanding, or that want to follow the latest state-of-the-art in PL research.

GHC internals

Core - Video lecture

STG

Category theory and co-things

https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/ First 6 chapters for now

https://www.olivierverdier.com/posts/2014/12/31/reader-writer-monad-comonad/

http://slides.com/fp-ctd/lecture-13#/44

http://hackage.haskell.org/package/comonad vs http://hackage.haskell.org/package/transformers

  • Reader vs Traced
  • Writer vs Env
  • State vs Store

Recursion and codata

https://tac-tics.net/blog/data-vs-codata

https://blog.sumtypeofway.com/posts/introduction-to-recursion-schemes.html and https://blog.sumtypeofway.com/posts/recursion-schemes-part-6.html

Very advanced typeclasses

  • Pipes/Coroutines/ConduitT/Stream - Pipes Tutorial, then try to understand Conduit space leaks
  • ContT (ugh, but necessary to have a basic understanding of)
  • Comonad, etc.

Higher-kinded types

Basic higher-kinded programming (heterogeneous lists and trees)

Singletons https://blog.jle.im/entry/introduction-to-singletons-1.html

Dependently-typed programming

Review singletons, including defunctionalisation (pointfree.io is useful)

Singletons P-* and S-* typeclasses, and using them

Recall: CPS for exposing existentials / passing proofs around, e.g. withSomeSing

Writing proofs as constraints https://www.youtube.com/watch?v=wNa3MMbhwS4

https://www.reddit.com/r/haskell/comments/g3lnk4/questions_on_actually_doing_dependentlytyped/

constraints package for avoiding CPS in more complex scenarios https://gist.github.com/Lysxia/2e110d801a1d1cccdf2863a7431709de

Effect systems

Free monad, freer monad

Polysemy & the general idea of "action/effect interpreters"

Eff & delimited continuations

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment