λ 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.

-- Hello World in Haskell main :: IO () main = putStrLn "λ Haskell 9.6 - CodeOrbitPro Pro Track" -- Basic types and functions double :: Int -> Int double x = x * 2 -- Interactive example (in GHCi) -- λ> double 21 -- 42

📊 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.

-- Type annotations (optional but good practice) age :: Int age = 25 pi :: Double pi = 3.141592653589793 name :: String name = "CodeOrbitPro" active :: Bool active = True -- Type inference x = 42 -- GHC infers Integer (or Int depending on defaulting) y = 3.14 -- Fractional => Double -- In GHCi: -- λ> :type x -- x :: Num a => a -- λ> :type active -- active :: Bool

⚙️ 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.

-- Basic function add :: Int -> Int -> Int add x y = x + y -- Using it result = add 3 4 -- 7 -- Partial application addFive :: Int -> Int addFive = add 5 -- addFive is `add 5` incrementAll :: [Int] -> [Int] incrementAll = map (+1) -- (+1) is a section -- Lambda multiply = \x y -> x * y -- Infix isEven n = n `mod` 2 == 0 -- main for testing main = print (addFive 10) -- 15

📋 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].

-- Creating lists numbers = [1,2,3,4,5] range1 = [1..10] -- [1,2,3,4,5,6,7,8,9,10] range2 = [2,4..10] -- [2,4,6,8,10] infinite = [1..] -- lazy infinite list -- Operations first = head numbers -- 1 (but careful with empty!) rest = tail numbers -- [2,3,4,5] taken = take 3 numbers -- [1,2,3] dropped = drop 2 numbers -- [3,4,5] -- List comprehension squares = [x*x | x <- [1..10], x `mod` 2 == 0] -- [4,16,36,64,100] -- Infinite list example firstTen = take 10 [1,3..] -- [1,3,5,7,9,11,13,15,17,19] -- main main = print firstTen

🔢 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.

-- Defining tuples point :: (Int, Int) point = (10, 20) person :: (String, Int, Bool) person = ("CodeOrbitPro", 2500, True) -- Access x = fst point -- 10 y = snd point -- 20 -- Pattern matching getName (name, _, _) = name -- zip and unzip names = ["Alice", "Bob", "Charlie"] scores = [90, 85, 95] pairs = zip names scores -- [("Alice",90), ("Bob",85), ("Charlie",95)] (unames, uscores) = unzip pairs -- Using curry/uncurry add (a,b) = a + b curriedAdd = curry add -- curriedAdd :: Int -> Int -> Int uncurriedAdd = uncurry curriedAdd main = print (getName person) -- "CodeOrbitPro"

🔀 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.

-- Pattern matching in function factorial :: Integer -> Integer factorial 0 = 1 factorial n = n * factorial (n - 1) -- List pattern sumList :: [Int] -> Int sumList [] = 0 sumList (x:xs) = x + sumList xs -- Maybe pattern safeDiv :: Int -> Int -> Maybe Int safeDiv _ 0 = Nothing safeDiv x y = Just (x `div` y) -- As-pattern duplicateHead :: [a] -> [a] duplicateHead list@(x:_) = x : list duplicateHead [] = [] -- Case expression describeList :: [a] -> String describeList xs = case xs of [] -> "Empty" [x] -> "Singleton" _ -> "Longer" -- main example main = print (duplicateHead [1,2,3]) -- [1,1,2,3]

🛡️ 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.

-- Guard example bmiTell :: Double -> String bmiTell bmi | bmi <= 18.5 = "Underweight" | bmi <= 25.0 = "Normal" | bmi <= 30.0 = "Overweight" | otherwise = "Obese" -- With where bmiTell' :: Double -> Double -> String bmiTell' weight height | bmi <= skinny = "Underweight" | bmi <= normal = "Normal" | bmi <= fat = "Overweight" | otherwise = "Obese" where bmi = weight / height ^ 2 skinny = 18.5 normal = 25.0 fat = 30.0 -- max using guards max' :: Ord a => a -> a -> a max' a b | a > b = a | otherwise = b main = print (bmiTell 24.5) -- Normal

🔄 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.

-- Basic recursion fibonacci :: Integer -> Integer fibonacci 0 = 0 fibonacci 1 = 1 fibonacci n = fibonacci (n-1) + fibonacci (n-2) -- Recursion on lists product' :: Num a => [a] -> a product' [] = 1 product' (x:xs) = x * product' xs -- Tail recursion with accumulator factorial' :: Integer -> Integer factorial' n = go n 1 where go 0 acc = acc go n acc = go (n-1) (n * acc) -- Mutual recursion even' :: Int -> Bool even' 0 = True even' n = odd' (n-1) odd' :: Int -> Bool odd' 0 = False odd' n = even' (n-1) main = print (factorial' 5) -- 120

⚡ 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).

import Data.List (foldl') -- Map and filter numbers = [1..10] squared = map (^2) numbers -- [1,4,9,16,25,36,49,64,81,100] evens = filter even numbers -- [2,4,6,8,10] -- Folds sum' = foldr (+) 0 numbers -- 55 product' = foldl (*) 1 numbers -- 3628800 -- ZipWith added = zipWith (+) [1,2,3] [4,5,6] -- [5,7,9] -- Function composition oddSquares = map (^2) . filter odd $ numbers -- or: (map (^2) . filter odd) numbers -- Custom HOF applyTwice :: (a -> a) -> a -> a applyTwice f x = f (f x) -- main main = print (applyTwice (*2) 5) -- 20

📦 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.

-- Import examples import Data.List (sort, nub, intercalate) import qualified Data.Map as M import Data.Char (toUpper) -- Using imported functions sorted = sort [3,1,4,1,5,9] -- [1,1,3,4,5,9] unique = nub [1,2,1,3,2] -- [1,2,3] joined = intercalate ", " ["Haskell", "is", "cool"] -- Data.Map phonebook = M.fromList [("Alice","123"), ("Bob","456")] alice = M.lookup "Alice" phonebook -- Just "123" -- Data.Char upper = map toUpper "haskell" -- "HASKELL" main = print upper

🏷️ 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) => ...

-- Algebraic data type data Point = Point Float Float deriving (Show, Eq) data Shape = Circle Point Float | Rectangle Point Point deriving (Show) area :: Shape -> Float area (Circle _ r) = pi * r ^ 2 area (Rectangle (Point x1 y1) (Point x2 y2)) = abs (x2 - x1) * abs (y2 - y1) -- Type class instance class Describable a where describe :: a -> String instance Describable Shape where describe (Circle _ r) = "Circle with radius " ++ show r describe (Rectangle _ _) = "Rectangle" -- Newtype newtype Age = Age Int deriving (Show, Eq, Ord) -- Usage circle = Circle (Point 0 0) 5 rect = Rectangle (Point 0 0) (Point 10 20) main = do print (area circle) print (describe rect)

💾 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.

-- Simple IO main :: IO () main = do putStrLn "Enter your name:" name <- getLine putStrLn ("Hello, " ++ name ++ "!") -- File IO writeExample = do writeFile "test.txt" "Haskell is great!" content <- readFile "test.txt" putStrLn content -- Sequence with return greet :: IO () greet = do putStrLn "What's your name?" name <- getLine let greeting = "Hello " ++ name putStrLn greeting -- IO with pure function pureAdd :: Int -> Int -> Int pureAdd x y = x + y main = do putStr "Enter first number: " x <- readLn :: IO Int putStr "Enter second: " y <- readLn print (pureAdd x y)

🔮 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.

-- Maybe monad safeDiv :: Int -> Int -> Maybe Int safeDiv _ 0 = Nothing safeDiv x y = Just (x `div` y) -- Using do with Maybe compute :: Int -> Int -> Int -> Maybe Int compute x y z = do a <- safeDiv x y b <- safeDiv a z return b -- List monad (non-deterministic) pairs :: [(Int, Int)] pairs = do x <- [1,2,3] y <- [4,5] return (x,y) -- all combinations -- Equivalent to list comprehension pairs' = [(x,y) | x <- [1,2,3], y <- [4,5]] -- IO monad (already seen) main = do putStrLn "Enter numbers:" input <- getLine let nums = map read (words input) :: [Int] print (sum nums) -- Writer monad (logging) import Control.Monad.Writer half :: Int -> Writer [String] Int half x = do tell ["Halving " ++ show x] return (x `div` 2) logCalc = do a <- half 8 b <- half a return b -- runWriter logCalc gives (2, ["Halving 8","Halving 4"])

🚀 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.

{-# LANGUAGE GADTs, DataKinds, KindSignatures #-} -- Simple GADT for expression evaluator data Expr a where I :: Int -> Expr Int B :: Bool -> Expr Bool Add :: Expr Int -> Expr Int -> Expr Int Eq :: Expr Int -> Expr Int -> Expr Bool eval :: Expr a -> a eval (I n) = n eval (B b) = b eval (Add e1 e2) = eval e1 + eval e2 eval (Eq e1 e2) = eval e1 == eval e2 -- Type families example {-# LANGUAGE TypeFamilies #-} type family Elem (c :: * -> *) :: * -- Lenses (using lens library) -- import Control.Lens -- data Person = Person { _name :: String, _age :: Int } -- makeLenses ''Person -- age .~ 30 $ person -- set age -- STM (Software Transactional Memory) import Control.Concurrent.STM transfer :: TVar Int -> TVar Int -> Int -> STM () transfer from to amount = do f <- readTVar from t <- readTVar to writeTVar from (f - amount) writeTVar to (t + amount) -- Example usage in IO main = do -- GADT test let expr = Add (I 5) (I 3) print (eval expr) -- 8 -- STM example account1 <- atomically $ newTVar 1000 account2 <- atomically $ newTVar 500 atomically $ transfer account1 account2 200 bal1 <- atomically $ readTVar account1 bal2 <- atomically $ readTVar account2 print (bal1, bal2) -- (800,700)