SyntaxStudy
Sign Up
R Functional Programming with Apply and purrr
R Beginner 1 min read

Functional Programming with Apply and purrr

R's functional programming idioms centre on applying a function to each element of a collection without writing explicit loops. The base apply family (lapply, sapply, vapply, tapply, mapply) covers most cases. vapply() is a safer alternative to sapply() because you declare the expected return type and length, so type mismatches fail loudly rather than silently. mapply() is the multivariate version, applying a function to corresponding elements from multiple vectors simultaneously. The purrr package from the tidyverse provides a more consistent, type-stable alternative to the base apply family. map() always returns a list; map_dbl(), map_chr(), map_lgl(), and map_int() return vectors of the specified type and throw an error if the type is wrong, making pipelines more robust. map2() and pmap() handle functions with two or multiple vector inputs respectively. Function composition allows you to build complex transformations out of simple, testable pieces. The pipe operator (|> in base R 4.1+, or %>% from magrittr/dplyr) passes the left-hand side as the first argument to the right-hand side, allowing you to write left-to-right chains of transformations instead of deeply nested function calls. Combining pipes with anonymous functions (the new \ shorthand in R 4.1+) produces clean, readable data transformation pipelines.
Example
# lapply — always returns a list
nums <- list(a = 1:5, b = 6:10, c = 11:15)
lapply(nums, mean)          # list of means

# sapply — simplifies if possible
sapply(nums, mean)          # named numeric vector: a=3 b=8 c=13

# vapply — type-safe sapply
vapply(nums, mean, FUN.VALUE = numeric(1))  # same but strict

# mapply — multivariate apply
mapply(function(x, y) x + y,
       x = c(1, 2, 3),
       y = c(10, 20, 30))   # 11 22 33

# Reduce and Filter (functional primitives)
Reduce("+", 1:5)                        # 15  (cumulative sum)
Reduce("+", 1:5, accumulate = TRUE)     # 1 3 6 10 15
Filter(function(x) x %% 2 == 0, 1:10)  # 2 4 6 8 10
Map(function(x, y) x * y,
    list(1,2,3), list(10,20,30))        # list(10, 40, 90)

# Base pipe (R >= 4.1)
c(3, 1, 4, 1, 5, 9) |> sort() |> unique() |> rev()
# 9 5 4 3 1

# Magrittr pipe style (common in older tidyverse code)
# library(dplyr)
# c(3,1,4,1,5,9) %>% sort() %>% unique() %>% rev()

# New backslash lambda syntax (R >= 4.1)
sapply(1:5, \(x) x^2)   # 1 4 9 16 25

# Partial application via wrapper functions
add_n <- function(n) function(x) x + n
add5 <- add_n(5)
sapply(1:4, add5)        # 6 7 8 9