Haskell Cheatsheet
Typeclasses
Use this Haskell reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Defining a Typeclass
class Describable a where describe :: a -> String shortName :: a -> String shortName = take 8 . describe -- default method
A typeclass is an interface for types. Methods may ship default implementations.
Instances
data User = User { userName :: String } instance Describable User where describe user = "User: " ++ userName user
Instances attach behavior to a concrete type.
Deriving Common Instances
data Status = Draft | Published | Archived deriving (Eq, Ord, Show, Read, Enum, Bounded) data Box a = Box a deriving (Show, Functor, Foldable, Traversable)
The Functor/Foldable/Traversable/Generic derivations are on by default under GHC2021.
Semigroup and Monoid
Semigroup is an associative combine (<>). Monoid adds an identity element (mempty).
[1,2] <> [3] -- [1,2,3] "foo" <> "bar" -- "foobar" Just [1] <> Just [2] -- Just [1,2] mempty :: String -- "" mconcat ["a","b","c"] -- "abc" foldMap show [1,2,3] -- "123", map then <> everything
Writing your own:
newtype MaxInt = MaxInt Int deriving (Eq, Show) instance Semigroup MaxInt where MaxInt a <> MaxInt b = MaxInt (max a b) instance Monoid MaxInt where mempty = MaxInt minBound
Functor
fmap (+1) (Just 2) -- Just 3 (+1) <$> Just 2 -- same thing, <$> is infix fmap fmap length ["a", "abc"] -- [1,3] fmap (*2) (Right 5) -- Right 10, maps the Right side only
Functor means you can map a pure function over a value in a context.
Applicative
pure (+) <*> Just 2 <*> Just 3 -- Just 5 (+) <$> Just 2 <*> Just 3 -- idiomatic applicative style liftA2 (,) (Just 1) (Just 2) -- Just (1,2) [(+1), (*2)] <*> [10,20] -- [11,21,20,40]
Applicative applies functions that are themselves inside a context. One Nothing anywhere makes the whole result Nothing.
Monad
Monad sequences computations where the next step depends on the previous result. >>= (bind) feeds a wrapped value into the next function, short-circuiting on failure:
half :: Int -> Maybe Int half n = if even n then Just (n `div` 2) else Nothing Just 20 >>= half >>= half -- Just 5 Just 20 >>= half >>= half >>= half -- Nothing (5 is odd)
Do Notation Desugars to >>=
quarter :: Int -> Maybe Int quarter n = do h <- half n q <- half h pure q -- exactly equivalent to: quarter' :: Int -> Maybe Int quarter' n = half n >>= \h -> half h >>= \q -> pure q
do works for every monad, not just IO. Each x <- action becomes a >>=, a bare action line becomes >>, and the last line is the result.
Writing a Monad Instance
Every Monad must also be a Functor and an Applicative:
newtype Identity a = Identity { runIdentity :: a } instance Functor Identity where fmap f (Identity a) = Identity (f a) instance Applicative Identity where pure = Identity Identity f <*> Identity a = Identity (f a) instance Monad Identity where Identity a >>= f = f a
Foldable and Traversable
Foldable reduces a structure (sum, length, elem, toList, foldr all work on any Foldable). Traversable maps an effectful function and collects the results:
import Text.Read (readMaybe) traverse readMaybe ["1","2","3"] :: Maybe [Int] -- Just [1,2,3] traverse readMaybe ["1","x","3"] :: Maybe [Int] -- Nothing sequenceA [Just 1, Just 2] -- Just [1,2], flips [f a] into f [a] sequenceA [Just 1, Nothing] -- Nothing mapM print [1,2,3] -- traverse specialized to Monad mapM_ print [1,2,3] -- same, discarding results traverse_ print [1,2,3] -- Foldable version (Data.Foldable)
Mnemonic: traverse = map + sequenceA. One failed element fails the whole traversal.