← Back to skills
extension
Category: Development & EngineeringAPI key requirement unconfirmed

haskell

Haskell pure functional programming with monads and type classes. Use for .hs files.

personAuthor: jakexiaohubgithub

Haskell

An advanced, purely functional programming language.

When to Use

  • Pure Functional Programming: Building systems with mathematical guarantees of purity, referential transparency, and immutability.
  • Compiler Construction & Domain-Specific Languages (DSLs): Authoring parsers, programming language compilers, and type checkers.
  • Financial Modeling & Cryptographic Protocols: Implementing smart contract runtimes (Cardano/Plutus) and quantitative financial models.
  • High-Assurance Systems: Mission-critical software where compile-time type verification must eliminate runtime errors entirely.

Quick Start

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

factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)

Core Concepts

Non-Strict Lazy Evaluation

Expressions are not evaluated until their results are explicitly demanded by consumer functions:

-- Generates an infinite stream of Fibonacci numbers lazily
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

-- Safely takes only the first 10 elements without evaluating infinity
main :: IO ()
main = print (take 10 fibs) -- [0,1,1,2,3,5,8,13,21,34]

Monads & Explicit I/O Separation

Isolates pure code from side effects (disk, network, state) using Monads:

-- Pure logic (no side-effects possible)
validateEmail :: String -> Either String String
validateEmail email
  | '@' `elem` email = Right email
  | otherwise        = Left "Invalid email format"

-- Explicit IO boundary
main :: IO ()
main = do
  putStrLn "Enter email address:"
  input <- getLine
  case validateEmail input of
    Right email -> putStrLn ("Success: " ++ email)
    Left err    -> putStrLn ("Error: " ++ err)

Advanced Typeclasses & Higher-Kinded Types

Defines generic behaviors across data types (Functor, Applicative, Monad):

-- Custom Functor mapping over container structure
data Tree a = Leaf a | Node (Tree a) (Tree a) deriving (Show)

instance Functor Tree where
  fmap f (Leaf x)   = Leaf (f x)
  fmap f (Node l r) = Node (fmap f l) (fmap f r)

Common Patterns

Monadic Error Handling with Either and Do Notation

Problem: Deeply nested error checks make business logic unreadable.

Solution: Compose operations cleanly using the Either monad:

data User = User { userId :: Int, email :: String } deriving (Show)

validateId :: Int -> Either String Int
validateId uid = if uid > 0 then Right uid else Left "Invalid User ID"

validateEmail :: String -> Either String String
validateEmail e = if '@' `elem` e then Right e else Left "Invalid Email"

createUser :: Int -> String -> Either String User
createUser uid e = do
    validId <- validateId uid
    validE  <- validateEmail e
    return (User validId validE)

Best Practices

Do:

  • Use GHC Modern Language Extensions: Standardize on GHC2021 or GHC2024 with OverloadedStrings and RecordWildCards.
  • Use Strict Data Types in Production: Use Data.Text instead of String ([Char]); use strict fields (!) in record data types.
  • Structure Applications with Polysemy or MTL: Manage effects cleanly using Monad Transformers (ReaderT, ExceptT) or effect systems.
  • Enforce Warnings with -Wall: Compile with -Wall -Werror to treat unhandled pattern cases as fatal build errors.

Don't:

  • Use head or fromJust: Partial functions crash at runtime on empty lists; use pattern matching or safe alternatives (headMay).
  • Use String for high-throughput text processing: The default String is a linked list of characters; use Text or ByteString.
  • Cause space leaks with lazy accumulation: Use strict fold (foldl') instead of lazy fold (foldl) to prevent building massive thunk trees.

Troubleshooting

| Error | Cause | Solution | | :---------------------------------------------------------- | :--------------------------------------------------- | :-------------------------------------------------------------------------------- | | Couldn't match expected type '...' with actual type '...' | Type mismatch in expression. | Check function signature and use GHC typed holes (_) to inspect required types. | | Non-exhaustive patterns in function | Pattern match missing one or more constructor cases. | Compile with -Wall to catch unhandled pattern match cases. | | Infinite loop / space leak on foldl | Lazy evaluation retaining thunk chains in memory. | Use strict fold foldl' from Data.List to force eager evaluation. |

References