1. Bonus Tutorials : Lambda Expressions in Haskell - wimsio/universities GitHub Wiki

Table of Contents

  1. Introduction to Lambda Expressions

  2. Lambda Syntax Basics

  3. Lambda as Anonymous Functions

  4. Lambdas in Higher-Order Functions

  5. Currying and Partial Application

  6. Advanced Lambda Techniques

    1. Multiple Arguments
    2. Nested Lambdas
    3. Lambdas with Pattern Matching
    4. Point-Free Style
  7. Performance and Style Tips

  8. Deeper Real-World Lambda Examples

    1. Sorting Complex Data
    2. Transforming Data in Pipelines
    3. Filtering Complex Conditions
    4. Lambda with Fold (Reduce)
    5. Working with Maybe/Just/Nothing
    6. Composing Functions on the Fly
    7. Using Lambdas with IO
    8. Lambdas for Event Handlers
  9. Summary and Next Steps

  10. Glossary of Terms


1. Introduction to Lambda Expressions

Lambda expressions let you create functions without naming them. They are also known as anonymous functions.

In Haskell, functions are first-class citizens—they can be passed as arguments, returned, and stored in variables.


2. Lambda Syntax Basics

Basic Syntax:

\x -> expression
  • \ = “lambda” symbol
  • x = argument(s)
  • -> = separates argument(s) from body
  • expression = function body

Example:

\x -> x + 1

Try in GHCi:

(\x -> x + 1) 4
-- Output: 5

3. Lambda as Anonymous Functions

Lambdas are anonymous—they don’t need names.

Example:

map (\x -> x * 2) [1,2,3]
-- Output: [2,4,6]

4. Lambdas in Higher-Order Functions

A higher-order function takes or returns another function.

Example:

filter (\x -> x `mod` 2 == 0) [1..10]
-- Output: [2,4,6,8,10]

5. Currying and Partial Application

  • Currying: All Haskell functions take one argument at a time.
  • Partial Application: Apply a function to some arguments, getting back another function.

Example:

let add = \x -> \y -> x + y
add 2 3       -- Output: 5

let addTwo = add 2
addTwo 5      -- Output: 7

6. Advanced Lambda Techniques

6.1 Multiple Arguments

Lambdas can take several arguments:

(\x y -> x * y) 3 4
-- Output: 12

6.2 Nested Lambdas

Lambdas can return other lambdas:

(\x -> (\y -> x + y)) 2 3
-- Output: 5

6.3 Lambdas with Pattern Matching

Use patterns in lambda arguments. (For \case, enable -XLambdaCase with :set -XLambdaCase.)

import Data.Maybe (mapMaybe)
map (\(a, b) -> a + b) [(1,2), (3,4)]
-- Output: [3,7]

Or using LambdaCase (GHC extension):

:set -XLambdaCase
let f = \case
      [] -> "Empty list"
      (x:xs) -> "Non-empty list"
f []      -- "Empty list"
f [1,2,3] -- "Non-empty list"

6.4 Point-Free Style

Remove explicit arguments:

map (\x -> x + 2) [1,2,3]    -- [3,4,5]
map (+2) [1,2,3]             -- [3,4,5]

7. Performance and Style Tips

  • Use lambdas for short, local logic.
  • Prefer named functions for complex tasks.
  • Deeply nested lambdas can reduce readability.
  • Lambdas aren’t always more efficient than named functions.

8. Deeper Real-World Lambda Examples

8.1 Sorting Complex Data

Sort a list of tuples by the second element:

import Data.List (sortBy)
let people = [("Alice", 30), ("Bob", 25), ("Carol", 27)]
let sortedByAge = sortBy (\(_, a1) (_, a2) -> compare a1 a2) people
sortedByAge
-- [("Bob",25),("Carol",27),("Alice",30)]

8.2 Transforming Data in Pipelines

Celsius to Fahrenheit:

let celsiusTemps = [0, 20, 30, 40]
let fahrenheitTemps = map (\c -> c * 9/5 + 32) celsiusTemps
fahrenheitTemps
-- [32.0,68.0,86.0,104.0]

8.3 Filtering Complex Conditions

Filter by salary:

let employees = [("Alice", 30000), ("Bob", 52000), ("Carol", 67000)]
let wellPaid = filter (\(_, salary) -> salary > 50000) employees
wellPaid
-- [("Bob",52000),("Carol",67000)]

8.4 Lambda with Fold (Reduce)

Sum only even numbers:

let numbers = [1..10]
let sumEvens = foldl (\acc x -> if even x then acc + x else acc) 0 numbers
sumEvens
-- 30

8.5 Working with Maybe/Just/Nothing

Extract and increment Just values:

import Data.Maybe (mapMaybe)
let values = [Just 1, Nothing, Just 3, Just 5, Nothing]
let incremented = mapMaybe (\mx -> case mx of Just x -> Just (x+1); Nothing -> Nothing) values
incremented
-- [2,4,6]

8.6 Composing Functions on the Fly

Capitalize words longer than 3 letters:

import Data.Char (toUpper)
let wordsList = ["cat", "elephant", "dog", "giraffe"]
let capitalized = map (\w -> if length w > 3 then map toUpper w else w) wordsList
capitalized
-- ["cat","ELEPHANT","dog","GIRAFFE"]

8.7 Using Lambdas with IO

Print each line with a number:

let linesList = ["Hello", "World", "Haskell"]
mapM_ (\(i, line) -> putStrLn (show i ++ ": " ++ line)) (zip [1..] linesList)
-- 1: Hello
-- 2: World
-- 3: Haskell

8.8 Lambdas for Event Handlers (GUI/Web)

Custom sort by string length:

import Data.List (sortBy)
let customSort = sortBy (\a b -> compare (length a) (length b))
customSort ["apple", "kiwi", "banana", "fig"]
-- ["fig","kiwi","apple","banana"]

9. Summary and Next Steps

  • Lambda expressions make code concise and flexible.
  • Essential for quick, local, or custom logic.
  • Study related topics: function composition, advanced pattern matching, higher-order programming.

10. Glossary of Terms

Term Meaning
Lambda expression A function without a name (\x -> x + 1)
Anonymous function Another term for a lambda expression
First-class citizen Functions can be used like values
Higher-order function A function that takes or returns other functions
Currying Transforming a multi-argument function into a chain of single-argument ones
Partial application Giving some arguments to a function to get a new function
Point-free style Writing functions without explicit arguments
Pattern matching Using data structure patterns in function definitions
GHCi Interactive Haskell shell (REPL)
REPL Read-Eval-Print Loop, interactive environment

Further Reading