forall a. (a -> a) -> a fix :: (a -> a) -> a class Functor f => Applicative f (>>=) :: Monad m => m a -> (a -> m b) -> m b fmap :: Functor f => (a -> b) -> f a -> f b pure :: Applicative f => a -> f a join :: Monad m => m (m a) -> m a
λ λ λ

haskell.monster

monster :: Monad m => m a -> m (Maybe a)

λ

Types Types Types

In Haskell, every expression has a type, and types are checked at compile time. The type system is based on Hindley-Milner type inference, which can deduce the most general type of any expression without requiring explicit annotations. Types are not constraints imposed on programs -- they are the logical structure that programs inhabit.

id :: a -> a
id x = x

Purity

Haskell functions are pure: given the same inputs, they always produce the same output, with no side effects. This referential transparency makes programs easier to reason about, test, and parallelize. Side effects are not eliminated -- they are controlled through the type system via monads, making the boundary between pure computation and effectful interaction explicit and checkable.

double :: Int -> Int
double x = x * 2
λ

Laziness Laziness Laziness

Haskell is lazy by default: expressions are not evaluated until their values are needed. This enables working with infinite data structures, composing operations without intermediate allocation, and defining control flow as ordinary functions. Laziness transforms the relationship between definition and execution -- you describe what to compute, and the runtime decides when and whether to compute it.

take 5 [1..] -- [1,2,3,4,5]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

Pattern Matching

Pattern matching decomposes data structures directly in function definitions, making the shape of data visible in the shape of code. Combined with algebraic data types, pattern matching gives Haskell an expressive power that blurs the line between computation and proof -- every exhaustive pattern match is a constructive proof that all cases have been considered.

length :: [a] -> Int
length [] = 0
length (_:xs) = 1 + length xs

Maybe Maybe Maybe

data Maybe a = Nothing | Just a

The simplest container of possibility. A value that might not be there. Maybe replaces null pointers with explicit, type-safe absence.


Either Either Either

data Either a b = Left a | Right b

Two possible outcomes, each carrying its own type. Either models computations that can fail with information about why they failed.


Monad Monad Monad

class Applicative m => Monad m where
  (>>=) :: m a -> (a -> m b) -> m b

The abstraction that chains computations. Bind takes a value in context and a function that produces a new context, composing effects sequentially while keeping the plumbing hidden.


Transformers Transformers Transformers

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }

When one monad is not enough. Transformers stack effects, layering state on top of IO on top of error handling, creating bespoke computation contexts from composable pieces.

The Monster Group The Monster Group The Monster Group

Order: ~8 × 1053 — The largest sporadic simple group in mathematics

Enter the type system