Study smarter with Fiveable
Get study guides, practice questions, and cheatsheets for all your subjects. Join 500,000+ students with a 96% pass rate.
Functional programming isn't just an academic exercise—it's the foundation for writing code that's easier to test, debug, and parallelize. You're being tested on your ability to recognize how these concepts work together: why pure functions enable referential transparency, how immutability prevents bugs in concurrent systems, and when lazy evaluation actually improves performance. These ideas show up everywhere from React's state management to distributed systems design.
Don't just memorize definitions—understand the underlying principles each concept demonstrates. Exam questions will ask you to identify which technique solves a specific problem, compare trade-offs between approaches, and write code that applies these patterns correctly. Master the why behind each concept, and you'll handle any FRQ they throw at you.
These concepts form the bedrock of functional programming by ensuring your code behaves consistently. When functions have no hidden dependencies or effects, reasoning about program behavior becomes straightforward.
Compare: Pure Functions vs. Referential Transparency—purity is the property of a function, while referential transparency is the consequence for expressions. If an FRQ asks about optimization or testing benefits, pure functions are your answer; for reasoning about code substitution, discuss referential transparency.
Functional programming handles state differently than imperative code. Instead of modifying existing data, you create new versions—making state changes explicit and trackable.
Compare: Immutability vs. Closures—both manage state, but differently. Immutability prevents any state change; closures encapsulate state within a function's environment. Use immutability for data flowing through your program, closures for maintaining context in callbacks or factories.
Treating functions as data unlocks powerful abstraction patterns. When functions can be passed around like any other value, you can build complex behavior from simple, composable pieces.
map, filter, and reduce are canonical examplesCompare: Higher-Order Functions vs. Function Composition—higher-order functions use other functions as data; composition combines functions into new functions. map(f, list) is higher-order; compose(g, f) creates a new function without applying it yet.
These techniques give you fine-grained control over how and when functions receive their arguments. By transforming function signatures, you create more reusable and expressive code.
Compare: Currying vs. Partial Application—currying restructures a function into nested unary functions; partial application pre-fills some arguments. Currying is automatic transformation; partial application is intentional argument binding. Both improve reusability but through different mechanisms.
When your code runs matters as much as what it computes. Lazy evaluation defers computation until results are actually needed, enabling patterns impossible with eager evaluation.
Compare: Lazy Evaluation vs. Recursion—both can process unbounded data, but differently. Recursion with lazy evaluation can define infinite streams; recursion with eager evaluation must terminate. Lazy evaluation controls when computation happens; recursion controls how problems decompose.
| Concept | Best Examples |
|---|---|
| Predictable behavior | Pure Functions, Referential Transparency |
| State management | Immutability, Closures |
| Functions as data | First-Class Functions, Higher-Order Functions |
| Building pipelines | Function Composition, Higher-Order Functions |
| Flexible application | Currying, Partial Application |
| Problem decomposition | Recursion, Function Composition |
| Performance optimization | Lazy Evaluation, Referential Transparency |
| Concurrent programming | Pure Functions, Immutability |
Which two concepts together guarantee that an expression can be safely replaced with its computed value throughout a program?
You need to create a specialized logging function from a general one by pre-filling the log level. Would you use currying or partial application? Explain the difference.
Compare and contrast how immutability and closures each handle state. In what scenario would you prefer one over the other?
A function reads from a database and returns user data. Is this function pure? What property does it violate, and what are the testing implications?
FRQ-style: Given a list processing task that may not need all results, explain how lazy evaluation combined with higher-order functions like map and filter could improve performance compared to eager evaluation.