λ Haskell Introduction
🎯 Complete Definition
Haskell is a purely functional, statically typed programming language named after logician Haskell Curry. It features lazy evaluation, type inference, and a strong type system. Created in 1990 by a committee of researchers, Haskell is the culmination of functional language research. Its design emphasizes correctness, conciseness, and mathematical rigor.
🔬 Core Characteristics
- Purely Functional: No side effects by default; functions always produce same output for same input
- Lazy Evaluation: Expressions evaluated only when needed (non-strict semantics)
- Static Typing: All types known at compile time, with powerful type inference
- Type Classes: Ad-hoc polymorphism (like interfaces but more powerful)
- Immutable Data: All values are immutable; no variables, only definitions
- GHC: Glasgow Haskell Compiler, the de facto standard (LLVM backend, parallel GC)
- Package System: Hackage (thousands of libraries) + Cabal/Stack
📊 Industry Usage
Haskell used in finance (Standard Chartered, Barclays), blockchain (Cardano, IOHK), web (Facebook's Sigma, WhatsApp), and academia. Known for high reliability and domain-specific languages (DSLs). Companies like GitHub, Google, and AT&T use Haskell in production. Strong in formal verification and critical systems.
📊 Basics & Types
🎯 Complete Definition
Haskell is statically typed with type inference. Every expression has a type determined at compile time. Basic types: Int (fixed precision), Integer (arbitrary precision), Float, Double, Bool, Char, () (unit type). Type annotations optional but recommended.
🏷️ Core Types
- Int: Fixed-precision (usually 64-bit) integers
- Integer: Arbitrary-precision integers (unbounded)
- Float: Single-precision floating point
- Double: Double-precision floating point
- Bool: True | False
- Char: Unicode characters ('' quotes)
- () : Unit type (like void, but a real type with single value)
- String: [Char] (list of characters)
🔧 Type Inference
Hindley-Milner type inference means you rarely need to write types, but they're always known. Use :type in GHCi. Type variables (a, b, c) denote polymorphism.
⚙️ Functions
🎯 Complete Definition
Functions in Haskell are first-class citizens. They take arguments and return values. All functions are curried by default (multiple arguments translated to series of single-argument functions). Function application via whitespace. Prefix notation by default; infix with backticks.
🏗️ Function Syntax
- add x y = x + y - basic function definition
- add' (x, y) = x + y - uncurried (tuple argument)
- add'' x y = x + y - curried form (actual Haskell style)
- add''' = \x y -> x + y - lambda (anonymous function)
- infix: `` `div` `` to use function as infix operator
- operator sections: (+1), (2*), (`elem` [1,2,3])
🔧 Partial Application
Because functions are curried, you can apply fewer arguments to get a specialized function. add 5 returns a function expecting the second argument.
📋 Lists & Ranges
🎯 Complete Definition
Lists are homogeneous, immutable sequences. They are the workhorse of Haskell. Syntax: [1,2,3], 1:2:3:[] (cons). Lists are linked lists (singly-linked). String is type synonym for [Char]. Ranges use .. notation.
🏗️ List Operations
- head, tail, init, last - basic access (partial functions!)
- null - check if empty
- length - O(n)
- ++ - concatenation
- : - cons (prepend)
- !! - indexing (partial)
- take, drop, splitAt - sublist extraction
- elem, notElem - membership
- replicate, cycle, repeat - generation
⚡ Ranges & List Comprehensions
[1..10], [2,4..20], ['a'..'z']. Infinite lists: [1..]. List comprehensions: [x^2 | x <- [1..10], even x].
🔢 Tuples
🎯 Complete Definition
Tuples are fixed-size, heterogeneous collections. Size is part of type. Common uses: returning multiple values, pairs (2-tuples), triples. Syntax: (1, "hello"), (True, 'a', 3.14). Access via pattern matching or fst, snd for pairs only.
🏗️ Tuple Operations
- fst (a,b) = a - first element of pair
- snd (a,b) = b - second element of pair
- curry/uncurry - convert between tuple and curried forms
- zip - combines two lists into list of pairs
- unzip - splits list of pairs into pair of lists
🔧 Pattern Matching
Tuples are often deconstructed in function arguments: addPair (x,y) = x + y. Nested tuples possible.
🔀 Pattern Matching
🎯 Complete Definition
Pattern matching is a fundamental feature that deconstructs data types and chooses branches based on structure. Used in function definitions, case expressions, and list comprehensions. Patterns can be literals, constructors, variables, wildcards (_), as-patterns (@).
🏗️ Pattern Types
- Literal patterns:
0,True,'a' - Constructor patterns:
Just x,Left err - Wildcard:
_matches anything, discards value - As-pattern:
xs@(x:rest)matches and binds both whole and parts - List patterns:
[],(x:xs) - Tuple patterns:
(a,b,c) - Guarded patterns: patterns with additional
| condition
🔧 Exhaustiveness
Patterns are checked for exhaustiveness; non-exhaustive patterns cause runtime error. Compiler warns about incomplete patterns.
🛡️ Guards
🎯 Complete Definition
Guards are boolean conditions in function definitions, providing a clean alternative to if-then-else chains. Guards use | after pattern, followed by condition and =. otherwise is a synonym for True. Guards checked top-down; first true wins.
🏗️ Guard Syntax
- function pattern | condition1 = result1
- | condition2 = result2
- | otherwise = defaultResult
🔧 Where Clauses
Often combined with where for local definitions. Guards and where make code readable.
🔄 Recursion
🎯 Complete Definition
Haskell has no loops; recursion is the primary iteration mechanism. Functions call themselves with modified arguments until base case. Tail recursion possible but not required due to laziness. Often combined with pattern matching. Many list functions are recursive.
🏗️ Recursive Patterns
- Base case: simplest input, stops recursion
- Recursive case: breaks problem into smaller piece + recursive call
- Accumulator: extra parameter to make tail recursion efficient
- Mutual recursion: functions calling each other
⚡ Examples
Recursion on lists: sum, product, length, map, filter all recursively defined. Fibonacci, factorial classic. Recursion on trees common.
⚡ Higher Order Functions
🎯 Complete Definition
Higher-order functions take functions as arguments or return functions. Core to functional programming. Common HOFs: map, filter, foldl/foldr, zipWith, takeWhile. Enable powerful abstractions and code reuse.
🏗️ Essential HOFs
- map :: (a -> b) -> [a] -> [b] - apply function to every element
- filter :: (a -> Bool) -> [a] -> [a] - keep elements satisfying predicate
- foldr :: (a -> b -> b) -> b -> [a] -> b - right-associative fold
- foldl :: (b -> a -> b) -> b -> [a] -> b - left-associative fold
- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] - combine lists pairwise
- takeWhile :: (a -> Bool) -> [a] -> [a] - take while predicate holds
- dropWhile :: (a -> Bool) -> [a] -> [a] - drop while predicate holds
🔧 Function Composition
(.) :: (b -> c) -> (a -> b) -> a -> c composes functions. ($) :: (a -> b) -> a -> b is function application (right-associative, low precedence).
📦 Modules
🎯 Complete Definition
Modules group related functions, types, and type classes. Standard modules: Prelude (implicitly imported), Data.List, Data.Maybe, Data.Map. Import syntax: import Module, qualified imports, selective imports. Cabal/Stack manage packages.
🏗️ Module Features
- import Data.List - brings all into scope
- import Data.List (sort, nub) - only specific functions
- import qualified Data.Map as M - qualified with alias
- import Data.Maybe (maybe) - selective
- module MyModule (exportList) where - define module with exports
🔧 Common Modules
Data.List (sort, group, permutations), Data.Char (isDigit, toUpper), Data.Map (Map), Data.Set, System.IO, Control.Monad. Hackage has thousands.
🏷️ Types & Classes
🎯 Complete Definition
Type classes define a set of operations that types can implement. Similar to interfaces. Standard classes: Eq, Ord, Show, Read, Num, Integral, Functor, Monad. Algebraic data types defined with data keyword.
🏗️ Data Declarations
- data Bool = False | True - sum type (enumeration)
- data Shape = Circle Float | Rect Float Float - with parameters
- type - type synonyms (aliases)
- newtype - wrapper with single constructor (runtime overhead zero)
- deriving - auto-implement type classes (Eq, Ord, Show, Read)
🔧 Type Class Inheritance
class (Eq a) => Ord a where ... means Ord implies Eq. Multiple constraints: (Show a, Num a) => ...
💾 IO
🎯 Complete Definition
Haskell separates pure and effectful code via IO type. IO a represents an action that, when performed, may do I/O and produce a value of type a. main :: IO () is program entry point. Actions sequenced using do notation.
🏗️ IO Operations
- putStrLn :: String -> IO () - print line
- print :: Show a => a -> IO () - print any Show value
- getLine :: IO String - read line
- readFile :: FilePath -> IO String - read file
- writeFile :: FilePath -> String -> IO () - write file
- return :: a -> IO a - lift pure value into IO
🔧 Do Notation
do x <- action1; action2; return (x+1) sequences IO. <- extracts result. let for pure bindings.
🔮 Monads
🎯 Complete Definition
Monads are a design pattern for structuring computations with context. Defined by type class Monad with >>= (bind) and return. Laws: left/right identity, associativity. Common monads: IO, Maybe, [] (list), Either, State, Reader, Writer.
🏗️ Monad Operations
- return :: a -> m a - inject value into monad
- >>= (bind) :: m a -> (a -> m b) -> m b - sequence with context
- >> - sequence ignoring result (>>= \_ -> ...)
- fail - error (often avoided)
- do notation - syntactic sugar for binds
🔧 Maybe Monad Example
Chaining operations that may fail: Just 3 >>= \x -> Just (x+1) yields Just 4. Nothing short-circuits.
🚀 Advanced Haskell
🎯 Complete Definition
Advanced features include GADTs (Generalized Algebraic Data Types), Type Families, Multi-parameter Type Classes, Functional Dependencies, Template Haskell (metaprogramming), Concurrency (forkIO, STM), FFI (foreign function interface), and Lazy/Strict annotations.
🏗️ Advanced Concepts
- GADTs: more precise type information for constructors
- Type Families: type-level functions
- DataKinds: promote data types to kinds
- Existential Quantification: hiding type parameters
- Concurrency: lightweight threads, MVars, STM
- Profunctors, Lenses: composable optics
- Free Monads: interpretable DSLs
⚡ Example: GADT
GADTs allow type arguments to vary per constructor.