Haskell

#30
TIOBE#29
PYPL#28
GitHub#75
programming languagefunctional programmingpure functionallazy evaluationtype systemmonads

Programming Language

Haskell

Overview

Haskell is a pure functional programming language characterized by a powerful type system, lazy evaluation, and mathematical elegance, serving both academic and practical purposes.

Details

Haskell is a pure functional programming language standardized by an academic committee in 1990, named after mathematician Haskell Curry. It adopts a pure functional paradigm where all computations are expressed as mathematical functions, and side effects are explicitly managed through the type system. It provides advanced features such as lazy evaluation (computation on demand), a powerful static type system, type inference, higher-order functions, pattern matching, algebraic data types, and monads. It is utilized in fields such as academic research, programming language theory, compiler design, cryptography, and financial systems, playing an important role in spreading functional programming concepts to other languages.

Code Examples

Hello World

-- Basic output
main :: IO ()
main = putStrLn "Hello, World!"

-- Multiple line output
main2 :: IO ()
main2 = do
    putStrLn "Hello, Haskell!"
    putStrLn "Pure functional programming language"
    putStrLn "Features lazy evaluation and powerful type system"

-- Output using variables
greetingProgram :: IO ()
greetingProgram = do
    let name = "John"
        age = 25
    putStrLn $ "My name is " ++ name ++ " and I am " ++ show age ++ " years old."

-- String interpolation style
formatGreeting :: String -> Int -> String
formatGreeting name age = "Name: " ++ name ++ ", Age: " ++ show age

main3 :: IO ()
main3 = putStrLn $ formatGreeting "Alice" 30

Data Types and Variables

-- Basic data types
number :: Int
number = 42

bigNumber :: Integer  -- Arbitrary precision integer
bigNumber = 12345678901234567890

floatValue :: Float
floatValue = 3.14

doubleValue :: Double
doubleValue = 3.141592653589793

character :: Char
character = 'A'

text :: String
text = "string"  -- String is an alias for [Char]

boolean :: Bool
boolean = True

-- Lists
numberList :: [Int]
numberList = [1, 2, 3, 4, 5]

stringList :: [String]
stringList = ["apple", "banana", "cherry"]

-- Infinite lists (due to lazy evaluation)
infiniteNumbers :: [Int]
infiniteNumbers = [1..]

-- List ranges
rangeList :: [Int]
rangeList = [1..10]

evenNumbers :: [Int]
evenNumbers = [2, 4..20]

-- Tuples
coordinate :: (Int, Int)
coordinate = (10, 20)

person :: (String, Int, Bool)
person = ("John Doe", 30, True)

-- Maybe type (null safety)
maybeName :: Maybe String
maybeName = Just "Alice"

noName :: Maybe String
noName = Nothing

-- Either type (error handling)
result :: Either String Int
result = Right 42

errorResult :: Either String Int
errorResult = Left "An error occurred"

-- Custom data types
data Color = Red | Green | Blue | RGB Int Int Int
    deriving (Show, Eq)

data Person = Person 
    { personName :: String
    , personAge :: Int
    , personEmail :: String
    } deriving (Show)

-- Usage examples
redColor :: Color
redColor = Red

customColor :: Color
customColor = RGB 255 128 0

john :: Person
john = Person "John Doe" 25 "[email protected]"

-- Type classes (interfaces)
class Describable a where
    describe :: a -> String

instance Describable Color where
    describe Red = "Red color"
    describe Green = "Green color"
    describe Blue = "Blue color"
    describe (RGB r g b) = "RGB(" ++ show r ++ "," ++ show g ++ "," ++ show b ++ ")"

Function Definition and Pattern Matching

-- Basic function definition
add :: Int -> Int -> Int
add x y = x + y

-- Curried function
addCurried :: Int -> (Int -> Int)
addCurried x = \y -> x + y

-- Pattern matching
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)

-- Guards (conditional branches)
classifyNumber :: Int -> String
classifyNumber n
    | n < 0     = "Negative number"
    | n == 0    = "Zero"
    | n <= 10   = "1 to 10"
    | otherwise = "Greater than 10"

-- List pattern matching
listLength :: [a] -> Int
listLength [] = 0
listLength (_:xs) = 1 + listLength xs

listHead :: [a] -> Maybe a
listHead [] = Nothing
listHead (x:_) = Just x

-- Complex pattern matching
processData :: Maybe (Either String Int) -> String
processData Nothing = "No data"
processData (Just (Left err)) = "Error: " ++ err
processData (Just (Right val)) = "Value: " ++ show val

-- Higher-order functions
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)

-- Function composition
compose :: (b -> c) -> (a -> b) -> a -> c
compose f g x = f (g x)

-- Operator definition
(.:) :: (b -> c) -> (a -> b) -> a -> c
(.:) = compose

-- Anonymous functions (lambdas)
square :: Int -> Int
square = \x -> x * x

-- where clause
calculateCircle :: Double -> (Double, Double)
calculateCircle r = (circumference, area)
  where
    circumference = 2 * pi * r
    area = pi * r * r

-- let expression
calculateRectangle :: Double -> Double -> Double
calculateRectangle width height = 
    let perimeter = 2 * (width + height)
        area = width * height
    in perimeter + area

-- Partial application
addFive :: Int -> Int
addFive = add 5

multiplyByTen :: Int -> Int
multiplyByTen = (*) 10

-- Usage examples
exampleUsage :: IO ()
exampleUsage = do
    print $ add 10 20
    print $ factorial 5
    print $ classifyNumber 7
    print $ listLength [1, 2, 3, 4]
    print $ applyTwice (+1) 5
    print $ addFive 15

List Processing and Higher-Order Functions

-- map function (apply function to each element)
squareList :: [Int] -> [Int]
squareList = map (^2)

-- filter function (extract elements matching condition)
evenOnly :: [Int] -> [Int]
evenOnly = filter even

-- fold function (reduce list to single value)
sumList :: [Int] -> Int
sumList = foldr (+) 0

productList :: [Int] -> Int
productList = foldr (*) 1

-- foldl (left fold)
reverseList :: [a] -> [a]
reverseList = foldl (flip (:)) []

-- List comprehensions
squares :: [Int]
squares = [x^2 | x <- [1..10]]

evenSquares :: [Int]
evenSquares = [x^2 | x <- [1..10], even x]

-- Conditional list comprehensions
pythagoreanTriples :: [(Int, Int, Int)]
pythagoreanTriples = [(a, b, c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]

-- zipWith (combine two lists)
addLists :: [Int] -> [Int] -> [Int]
addLists = zipWith (+)

-- take (get first n elements)
firstFive :: [a] -> [a]
firstFive = take 5

-- drop (remove first n elements)
skipFive :: [a] -> [a]
skipFive = drop 5

-- concat (flatten list of lists)
flattenList :: [[a]] -> [a]
flattenList = concat

-- concatMap (map then concat)
duplicateElements :: [a] -> [a]
duplicateElements = concatMap (\x -> [x, x])

-- Custom higher-order function
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
mapMaybe _ [] = []
mapMaybe f (x:xs) = case f x of
    Nothing -> mapMaybe f xs
    Just y  -> y : mapMaybe f xs

-- Infinite list processing (lazy evaluation)
fibonacci :: [Int]
fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)

primes :: [Int]
primes = sieve [2..]
  where
    sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

-- Usage examples
listExamples :: IO ()
listExamples = do
    print $ squareList [1, 2, 3, 4, 5]
    print $ evenOnly [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    print $ sumList [1, 2, 3, 4, 5]
    print $ take 10 squares
    print $ take 10 fibonacci
    print $ take 10 primes
    print $ addLists [1, 2, 3] [4, 5, 6]

Monads and do-notation

-- Maybe monad
safeDivide :: Double -> Double -> Maybe Double
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)

safeSquareRoot :: Double -> Maybe Double
safeSquareRoot x
    | x < 0     = Nothing
    | otherwise = Just (sqrt x)

-- Maybe monad chaining
safeCalculation :: Double -> Double -> Maybe Double
safeCalculation x y = do
    result1 <- safeDivide x y
    result2 <- safeSquareRoot result1
    return (result2 * 2)

-- Or using bind operator
safeCalculation' :: Double -> Double -> Maybe Double
safeCalculation' x y = 
    safeDivide x y >>= safeSquareRoot >>= \result -> return (result * 2)

-- List monad
combinations :: [Int] -> [Int] -> [(Int, Int)]
combinations xs ys = do
    x <- xs
    y <- ys
    guard (x + y > 5)  -- Filter condition
    return (x, y)

-- IO monad
getUserInput :: IO (String, Int)
getUserInput = do
    putStrLn "Enter your name:"
    name <- getLine
    putStrLn "Enter your age:"
    ageStr <- getLine
    let age = read ageStr :: Int
    return (name, age)

interactiveProgram :: IO ()
interactiveProgram = do
    (name, age) <- getUserInput
    putStrLn $ "Hello, " ++ name ++ "!"
    putStrLn $ "You are " ++ show age ++ " years old."
    when (age >= 20) $ putStrLn "You are an adult!"

-- Custom monad (State monad example)
newtype State s a = State { runState :: s -> (a, s) }

instance Functor (State s) where
    fmap f (State g) = State $ \s -> let (a, s') = g s in (f a, s')

instance Applicative (State s) where
    pure a = State $ \s -> (a, s)
    (State f) <*> (State g) = State $ \s -> 
        let (f', s')  = f s
            (a, s'')  = g s'
        in (f' a, s'')

instance Monad (State s) where
    return = pure
    (State g) >>= f = State $ \s ->
        let (a, s') = g s
            (State h) = f a
        in h s'

get :: State s s
get = State $ \s -> (s, s)

put :: s -> State s ()
put s = State $ \_ -> ((), s)

-- State monad usage example
counter :: State Int Int
counter = do
    count <- get
    put (count + 1)
    return count

runCounter :: (Int, Int)
runCounter = runState (counter >> counter >> counter) 0

Functors and Applicatives

-- Functor type class
-- fmap :: (a -> b) -> f a -> f b

-- Maybe Functor usage examples
functorExamples :: IO ()
functorExamples = do
    print $ fmap (*2) (Just 5)        -- Just 10
    print $ fmap (*2) Nothing         -- Nothing
    print $ fmap show [1, 2, 3]       -- ["1", "2", "3"]

-- Custom Functor
data Box a = Box a deriving (Show)

instance Functor Box where
    fmap f (Box x) = Box (f x)

-- Applicative Functor
-- pure :: a -> f a
-- (<*>) :: f (a -> b) -> f a -> f b

applicativeExamples :: IO ()
applicativeExamples = do
    print $ pure (+) <*> Just 3 <*> Just 5     -- Just 8
    print $ pure (+) <*> [1, 2] <*> [10, 20]   -- [11, 21, 12, 22]

-- Combining multiple Maybes
combineThree :: Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int
combineThree mx my mz = (+) <$> mx <*> ((*) <$> my <*> mz)

-- Alternative type class
-- empty :: f a
-- (<|>) :: f a -> f a -> f a

alternativeExamples :: IO ()
alternativeExamples = do
    print $ Nothing <|> Just 5 <|> Just 3    -- Just 5
    print $ [] <|> [1, 2] <|> [3, 4]         -- [1, 2]

-- Traversable (combining list processing with monads)
-- traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

traversableExamples :: IO ()
traversableExamples = do
    print $ traverse safeDivide [10, 8, 6] <*> pure 2  -- Just [5.0, 4.0, 3.0]
    print $ traverse (\x -> if x > 0 then Just x else Nothing) [1, 2, -3]  -- Nothing

Type-Level Programming

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}

-- Type-level natural numbers
data Nat = Zero | Succ Nat

-- Length-indexed vectors
data Vec (n :: Nat) a where
    VNil  :: Vec 'Zero a
    VCons :: a -> Vec n a -> Vec 'Succ n a

-- Type-safe head function
vHead :: Vec ('Succ n) a -> a
vHead (VCons x _) = x

-- Type-safe tail function
vTail :: Vec ('Succ n) a -> Vec n a
vTail (VCons _ xs) = xs

-- Vector concatenation
vAppend :: Vec n a -> Vec m a -> Vec (Add n m) a
vAppend VNil ys = ys
vAppend (VCons x xs) ys = VCons x (vAppend xs ys)

-- Type-level addition
type family Add (n :: Nat) (m :: Nat) :: Nat where
    Add 'Zero m = m
    Add ('Succ n) m = 'Succ (Add n m)

-- Phantom Types
newtype Tagged tag a = Tagged a deriving (Show)

-- Type safety using tags
data UserID
data ProductID

createUser :: String -> Tagged UserID Int
createUser name = Tagged 123  -- Actually save to DB

createProduct :: String -> Tagged ProductID Int
createProduct name = Tagged 456  -- Actually save to DB

-- Prevent mixing different ID types at type level
-- getUserById :: Tagged UserID Int -> User
-- getProductById :: Tagged ProductID Int -> Product

-- Generalized Algebraic Data Types (GADTs)
data Expr a where
    LitInt  :: Int -> Expr Int
    LitBool :: Bool -> Expr Bool
    Add     :: Expr Int -> Expr Int -> Expr Int
    If      :: Expr Bool -> Expr a -> Expr a -> Expr a

-- Type-safe evaluator
eval :: Expr a -> a
eval (LitInt n) = n
eval (LitBool b) = b
eval (Add e1 e2) = eval e1 + eval e2
eval (If cond then_ else_) = if eval cond then eval then_ else eval else_

-- Usage example
exampleExpr :: Expr Int
exampleExpr = If (LitBool True) (Add (LitInt 1) (LitInt 2)) (LitInt 0)

Special Features

Lazy Evaluation

-- Infinite data structures
ones :: [Int]
ones = 1 : ones

naturals :: [Int]
naturals = [0..]

-- Only computed as needed due to lazy evaluation
takeFirst10 :: [Int]
takeFirst10 = take 10 naturals

-- Fibonacci sequence (infinite)
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

-- Infinite list of primes (Sieve of Eratosthenes)
sieve :: [Int] -> [Int]
sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

primes :: [Int]
primes = sieve [2..]

-- Lazy I/O
lazyReadFile :: FilePath -> IO String
lazyReadFile = readFile  -- Doesn't load entire file into memory at once

-- Conditional computation (executed only when needed)
expensiveCalculation :: Int -> Int
expensiveCalculation n = if n > 1000 then n * n else 0

conditionalComputation :: Int -> Int
conditionalComputation n = 
    let expensive = expensiveCalculation n
    in if n > 500 then expensive else n

-- Streaming processing with take/drop combinations
processChunks :: [a] -> [[a]]
processChunks [] = []
processChunks xs = take 10 xs : processChunks (drop 10 xs)

-- Parallel processing of infinite lists
parallelSum :: [Int] -> Int
parallelSum xs = foldr (+) 0 xs  -- Efficient due to lazy evaluation

Type Classes and Polymorphism

-- Basic type classes
class Eq a where
    (==) :: a -> a -> Bool
    (/=) :: a -> a -> Bool
    x /= y = not (x == y)

class Eq a => Ord a where
    compare :: a -> a -> Ordering
    (<), (<=), (>), (>=) :: a -> a -> Bool
    max, min :: a -> a -> a

-- Custom type class
class Serializable a where
    serialize :: a -> String
    deserialize :: String -> Maybe a

instance Serializable Int where
    serialize = show
    deserialize s = case reads s of
        [(x, "")] -> Just x
        _         -> Nothing

instance Serializable Bool where
    serialize True = "T"
    serialize False = "F"
    deserialize "T" = Just True
    deserialize "F" = Just False
    deserialize _   = Nothing

-- Polymorphic functions
identity :: a -> a
identity x = x

first :: (a, b) -> a
first (x, _) = x

swap :: (a, b) -> (b, a)
swap (x, y) = (y, x)

-- Constrained polymorphism
showAndCompare :: (Show a, Ord a) => a -> a -> String
showAndCompare x y = show x ++ " " ++ comparison ++ " " ++ show y
  where
    comparison = case compare x y of
        LT -> "<"
        EQ -> "=="
        GT -> ">"

-- Higher-Kinded Types
class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Functor f => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

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

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

instance Monad m => Functor (ReaderT r m) where
    fmap f (ReaderT g) = ReaderT $ \r -> fmap f (g r)

instance Monad m => Applicative (ReaderT r m) where
    pure a = ReaderT $ \_ -> pure a
    (ReaderT f) <*> (ReaderT g) = ReaderT $ \r -> f r <*> g r

instance Monad m => Monad (ReaderT r m) where
    (ReaderT g) >>= f = ReaderT $ \r -> do
        a <- g r
        runReaderT (f a) r

-- Type families
type family Element c :: * where
    Element [a] = a
    Element (Maybe a) = a
    Element String = Char

class Collection c where
    empty :: c
    insert :: Element c -> c -> c
    member :: Element c -> c -> Bool

instance Collection [a] where
    empty = []
    insert = (:)
    member = elem

Parser Combinators

-- Simple parser library
newtype Parser a = Parser { parse :: String -> Maybe (a, String) }

instance Functor Parser where
    fmap f (Parser p) = Parser $ \input -> do
        (result, rest) <- p input
        return (f result, rest)

instance Applicative Parser where
    pure a = Parser $ \input -> Just (a, input)
    (Parser pf) <*> (Parser pa) = Parser $ \input -> do
        (f, rest1) <- pf input
        (a, rest2) <- pa rest1
        return (f a, rest2)

instance Monad Parser where
    (Parser p) >>= f = Parser $ \input -> do
        (result, rest) <- p input
        parse (f result) rest

-- Basic parsers
char :: Char -> Parser Char
char c = Parser $ \input ->
    case input of
        (x:xs) | x == c -> Just (c, xs)
        _               -> Nothing

string :: String -> Parser String
string "" = pure ""
string (c:cs) = (:) <$> char c <*> string cs

digit :: Parser Int
digit = Parser $ \input ->
    case input of
        (x:xs) | x >= '0' && x <= '9' -> Just (read [x], xs)
        _                            -> Nothing

-- Combinators
(<|>) :: Parser a -> Parser a -> Parser a
(Parser p1) <|> (Parser p2) = Parser $ \input ->
    case p1 input of
        Nothing -> p2 input
        result  -> result

many :: Parser a -> Parser [a]
many p = some p <|> pure []

some :: Parser a -> Parser [a]
some p = (:) <$> p <*> many p

-- Number parser
number :: Parser Int
number = read <$> some digit

-- Expression parser example
data Expr = Num Int | Add Expr Expr | Mul Expr Expr deriving (Show)

parseExpr :: Parser Expr
parseExpr = parseAdd

parseAdd :: Parser Expr
parseAdd = chainLeft parseMul (Add <$ char '+')

parseMul :: Parser Expr
parseMul = chainLeft parseFactor (Mul <$ char '*')

parseFactor :: Parser Expr
parseFactor = Num <$> number
          <|> (char '(' *> parseExpr <* char ')')

chainLeft :: Parser a -> Parser (a -> a -> a) -> Parser a
chainLeft p op = p >>= rest
  where
    rest a = (do f <- op
                 b <- p
                 rest (f a b)) <|> pure a

-- Usage example
parseExample :: String -> Maybe Expr
parseExample input = fst <$> parse parseExpr input

Versions

Version Status Key Features Release Year
Haskell 2010 Current Current standard, improved module system 2010
Haskell 98 Legacy First stable standard 1998
GHC 9.4 Latest Compiler Performance improvements, better type errors 2022
GHC 9.2 Stable Compiler Record Dot Syntax, type family improvements 2021

Reference Pages

Official Documentation

Learning Resources

Frameworks and Libraries

  • Yesod - Web framework
  • Snap - Lightweight web framework
  • Pandoc - Document conversion tool