Functions & Parameters
A function packages a reusable block of logic under a name, optionally accepting inputs (parameters) and producing an output (a return value). Functions are the primary tool for breaking a large program into smaller, understandable, testable pieces.
A function is a named, reusable block of code that performs a specific task. Parameters are the named inputs a function accepts when it's called; a return value is the result the function sends back to whoever called it.
Functions let you write logic once and reuse it anywhere it's needed, instead of copying and pasting the same code repeatedly. They also let you name a piece of logic clearly (calculateTotalPrice() is more meaningful than the raw calculation inline), and let you test that logic in isolation.
Calculating a total price, validating an email address's format, converting a temperature between units, sending a network request — nearly every meaningful piece of program logic is organized into functions.
A function is like a vending machine: you put in specific inputs (coins and a selection — the parameters), and it hands back a specific output (a snack — the return value), following the same internal process every single time, regardless of who's using it.
add is defined once, taking two parameters a and b, and returning their sum. Calling add(3, 4) passes 3 and 4 as arguments, and the function's return value (7) is stored in result.
A parameter is the name used inside a function's definition; an argument is the actual value supplied when the function is called — the two terms are often used loosely interchangeably, but technically refer to different sides of the same call.
Pass-by-value copies an argument's value into the function's parameter, so changes inside the function don't affect the caller's original variable; pass-by-reference (or passing a reference/pointer) lets the function affect the caller's original data directly. Different languages default to different behavior, and some (like Python) use a model often described as 'pass-by-object-reference' that behaves like pass-by-value for immutable types and like pass-by-reference for mutable ones.
Default parameter values let a function be called with fewer arguments than it formally accepts, falling back to a specified default for any omitted ones. Variadic functions (using ...args, *args, or similar) accept an arbitrary number of arguments.
A pure function's return value depends only on its input arguments, with no observable side effects (no modifying external state, no I/O) — pure functions are easier to test, reason about, and safely run in parallel, a theme expanded in the future Functional Programming module.
Function overloading (same function name, different parameter types/counts, resolved at compile time — available in Java, C++) is a different mechanism from default parameters, and not supported at all in some languages (like Python or JavaScript, which instead rely on default/variadic parameters or runtime type checks).
Assuming pass-by-value everywhere and being surprised when passing a mutable object (a list, a dictionary) to a function in Python or JavaScript lets that function modify the caller's original data.
Writing functions that do far too much at once (validate input, calculate a result, format output, log something, and save to a database, all in one function) instead of splitting responsibilities into smaller, focused functions.
Forgetting a return statement and being surprised the function's result is None/undefined/void rather than the intended computed value.
Relying on side effects inside a function that's used or tested as if it were pure, making the function's behavior depend on hidden external state that isn't obvious from its signature.
Writing small, single-purpose functions with clear names, so each one can be tested, reused, and understood in isolation without needing to read the whole program.
Using default parameter values to give a function sensible behavior out of the box, while still allowing callers to override specific options when needed.
Preferring pure functions for core business logic wherever practical, isolating unavoidable side effects (like database writes or network calls) into a smaller, clearly identified set of functions.
Calling conventions and the function call itself At the machine-code level, calling a function involves a specific, architecture-defined protocol (a calling convention) for where arguments are placed (in registers and/or on the stack), where the return value goes, which registers the callee must preserve, and how control returns to the caller afterward — covered fully in the future ABI and Calling Conventions module.
Higher-order functions and closures A function that accepts another function as an argument, or returns a function as its result, is a higher-order function — foundational to functional programming and used pervasively for callbacks, event handlers, and functional-style data transformations (map/filter/reduce). A closure is a function bundled together with references to variables from its enclosing scope at the time it was created, letting it 'remember' that environment even after the outer function has returned — covered further in the Scope & Lifetime section and the future Functional Programming module.
Recursion, tail calls, and stack usage Every non-tail-recursive function call typically consumes a new stack frame, and enough nested calls without returning can exhaust the call stack (a stack overflow). Some languages and compilers can optimize a specific pattern — tail calls, where a function's very last action is calling another function — into a simple jump that reuses the current stack frame instead of growing it, a topic explored fully in the following Recursion section and the future Memory & Program Execution module.
What's the difference between a parameter and an argument?
A parameter is the named placeholder declared in a function's definition (def add(a, b)); an argument is the actual value supplied at the call site (add(3, 4) passes 3 and 4 as arguments for a and b).
What's the difference between pass-by-value and pass-by-reference?
Pass-by-value copies the argument's value into the function, so modifications inside the function don't affect the caller's original variable. Pass-by-reference passes a reference to the original data, so modifications inside the function do affect the caller's original variable.
Output-based: in Python, why does a function that appends to a list parameter affect the caller's original list, while reassigning a number parameter does not?
Because Python passes object references — for mutable objects like lists, mutating methods (append) change the shared underlying object, visible to the caller. For immutable types like numbers, 'reassigning' the parameter inside the function just makes the local name point to a new value, leaving the caller's original variable, and the object it referenced, unaffected.
What makes a function 'pure'?
Its return value depends only on its input arguments (given the same inputs, it always returns the same output), and it has no observable side effects — it doesn't modify external state, perform I/O, or depend on anything other than its parameters.
def add(a, b):
return a + b
result = add(3, 4)
print(result) # 7