Programming Languages and Paradigms

Follow a program from the code a person writes to the binary instructions a processor runs, and see how compilers, interpreters, bytecode and just-in-time compilers handle that translation. Learn to tell a language's syntax from its meaning, how languages check data types, and where a running program keeps its data and frees its memory. Then compare four ways of writing programs (imperative, object-oriented, functional and declarative) by solving the same problems in each, and learn how to choose a language that fits a project.

BeginnerLesson 5 of 7 in Introduction to Computer Science Course

What you will learn

  1. Follow a program from the code a person writes down to the binary instructions a processor carries out, and compare the ways that translation can happen: ahead of time, while the program runs, or in a mix of both.
    • Compare machine code, assembly language and high-level languages
      Describe the three levels at which programs can be written, show how one line of high-level code maps onto several machine instructions, and explain why most software is written at the highest level.
    • Explain what a compiler does
      Describe how a compiler translates a whole program ahead of time, name its main stages, and explain what happens to errors and to the finished executable.
    • Explain interpreters, bytecode and just-in-time compilation
      Describe how an interpreter runs a program, how languages such as Java and Python combine compiling and interpreting, and how to weigh the trade-offs between these approaches.
  2. Separate the rules for how code must be written from the rules for what it does, recognize the building blocks that most high-level languages share, and trace short programs to predict their results.
    • Distinguish syntax from semantics
      Explain the difference between the form of a program and its meaning, tell syntax errors, runtime errors and logic errors apart, and spot where languages give the same-looking code a different meaning.
    • Trace the shared building blocks of high-level programs
      Recognize the constructs found in nearly every high-level language (variables, expressions, control flow and functions with their scope) and follow a short program step by step to predict its output.
  3. Describe how languages classify and check values by type, where a running program keeps its data, and how memory is reserved and released, by the programmer or automatically, with the safety and speed trade-offs each choice brings.
    • Compare how languages use and check data types
      Explain what a data type is, compare static and dynamic typing and strict and loose conversion rules, and predict how common languages treat mixed types and number limits.
    • Describe where a running program keeps its data
      Explain variables as named memory locations, the roles of the call stack and the heap, and the difference between copying a value and sharing a reference.
    • Compare manual and automatic memory management
      Explain how memory is released in languages where the programmer manages it and in languages with garbage collection or ownership rules, and connect memory errors to program crashes and security flaws.
  4. Describe programs as sequences of commands that change stored state, then as collections of objects that bundle data with behavior, and use classes, encapsulation, inheritance and polymorphism to organize code.
    • Describe the imperative and procedural style
      Explain imperative programming as step-by-step commands that change a program's state, show how procedures and structured control flow organize those commands, and recognize the risks of shared changing state.
    • Model data and behavior with classes and objects
      Explain how object-oriented programming groups data and the operations on it into objects, read and design a simple class, and use encapsulation to protect an object's data.
    • Reuse and extend classes with inheritance, polymorphism and composition
      Use inheritance to build specialized classes, explain how polymorphism lets one call behave differently for different objects, and decide when to combine objects instead of inheriting.
  5. Build programs from functions that return new values instead of changing old ones, and write programs that state the result wanted while a language engine decides how to compute it.
    • Build programs from pure functions and immutable data
      Explain pure functions, immutability, first-class and higher-order functions, and recursion, and rewrite a loop as a pipeline of map, filter and reduce.
    • Describe the result with declarative languages
      Explain declarative programming as stating what result is wanted rather than how to get it, and read examples in a database query language, a logic language and other declarative notations.
  6. Solve the same problem in different styles, recognize that most modern languages mix paradigms, and weigh the technical and practical factors that decide which language suits a project.
    • Solve one problem in several paradigms
      Write the same small task in imperative, object-oriented, functional and declarative styles, compare what each version makes easy, and recognize multi-paradigm languages.
    • Choose a programming language for a task
      Weigh the factors that decide which language suits a project (platform, domain, performance, safety, ecosystem and people) and justify a choice for a concrete scenario.

Programming Languages and Paradigms study guide

6 chapters

1

From source code to running instructions

A processor runs small binary instructions, while people usually write programs in languages built around the problem they want to solve. Translators connect these two levels. Follow the same calculation through those layers, then compare building a program before it runs with interpreting it or compiling parts during execution. The route affects portability, startup time, speed, and when an error can appear.

1.1 Three levels of program code

A line such as x = a + b names a calculation without mentioning a processor register. Abstraction makes this possible: a language hides lower-level details so that a programmer can focus on the work the program should do.

What the processor runs

Machine code consists of instructions encoded as binary numbers. A processor's instruction set architecture (ISA) defines its instructions and their encodings. x86-64 is common in PCs and servers; ARM is common in phones, tablets, and many laptops. Code built for one ISA needs translation or emulation to work on another.

Hexadecimal makes instruction bytes shorter to display. On x86-64, B8 01 00 00 00 puts 1 into the register EAX. The bytes 01 D8 add the value in EBX to EAX. Registers are small storage locations inside the processor. Each instruction performs a small action, such as moving data, adding, comparing, or jumping. The fetch-decode-execute cycle runs these instructions.

Names for the same small actions

Assembly language replaces instruction bytes with readable names called mnemonics. Those two x86-64 instructions can be written:

mov eax, 1
add eax, ebx

An assembler converts assembly into machine code. An ordinary instruction line normally produces one machine instruction. Register names and instructions still belong to a particular ISA, so using words instead of bytes does not make assembly portable.

Describe a problem in fewer lines

A high-level language provides variables, expressions, conditions, loops, and functions. Python, C, Java, and JavaScript let the programmer describe operations without selecting registers. One statement commonly requires several machine instructions. A simplified translation of x = a + b would:

  1. Load the value of a into a register.
  2. Load the value of b into another register.
  3. Add the register values.
  4. Store the result for x.

A real compiler may combine or rearrange those steps while keeping the required behavior.

LevelWhat a programmer writesWhat handles the hardware details?
Machine codeEncoded processor instructionsThe programmer or code generator
AssemblyMnemonics and registersThe programmer, with an assembler encoding instructions
High-level codeVariables and operations on the problem's dataA compiler, interpreter, or a combination

High-level source is easier to read, change, and check. Portable C source can be compiled once for an x86-64 system and again for an ARM system. Each build still needs the right operating-system support and libraries. Portability of source does not make a native executable universal.

Why the lower levels still matter

Assembly remains useful in startup code, some operating-system and driver routines, small embedded systems, and security analysis of programs with no available source. For ordinary application code, modern compilers usually produce machine code with performance comparable to handwritten assembly.

The shift toward higher levels happened gradually. Assembly spread in the late 1940s and early 1950s. Grace Hopper's A-0 system in 1952 helped advance automatic translation. IBM released FORTRAN in 1957; COBOL followed in 1959, and Lisp emerged around 1958–1960. "High-level" names the distance from hardware details, not the difficulty of learning the language.

Key Takeaways

  • A processor executes machine instructions defined by its ISA.
  • An assembler encodes assembly instructions; assembly remains tied to an ISA.
  • High-level statements hide hardware steps and commonly become several instructions.
  • Portable source can be translated for different targets; native machine code is target-specific.
  • Low-level code remains useful where hardware access or analysis requires it.

1.2 How a compiler builds a program

A compiler translates source code into target code. The target may be native machine code or an intermediate form such as bytecode. Ahead-of-time (AOT) compilation happens before the program runs; just-in-time compilation happens during execution.

From text to instructions

For an ahead-of-time build, several stages turn a source file into instructions:

  1. Lexical analysis splits text into tokens: meaningful pieces such as names, operators, and numbers.
  2. Parsing checks the grammar and builds a structure showing how the pieces fit together.
  3. Semantic analysis checks rules about meaning, including declarations and allowed type combinations.
  4. Optimization finds a more efficient way to perform the required work.
  5. Code generation emits the target instructions.

Consider the C statement total = price * 3;. Its six tokens are total, =, price, *, 3, and ;. The names are identifiers, 3 is an integer literal, and the semicolon ends the statement. Parsing records that multiplication belongs inside the assignment: calculate price * 3, then store its value in total.

These stages have different jobs. A correctly spelled sequence of tokens can still violate the grammar. A grammatical statement can still misuse a type. Finding tokens is not enough to establish that a program is valid.

Make the same work cheaper

Suppose source code contains seconds_per_day = 60 * 60 * 24. The factors are constants, so the compiler can multiply 60 by 60 to get 3600, then multiply by 24 to get 86400 during the build. It can place the final value in the output instead of repeating the multiplication at run time. This is constant folding.

An optimization must preserve the behavior the language requires. It cannot replace a calculation with a faster one that gives different required results. The aim is to reduce work, time, or memory while keeping the program's meaning.

Join the pieces

Large programs are usually built from many files. A compiler can produce an object file for each source file. An object file contains translated code that may still need connections to other code. A linker joins the pieces and resolves calls to library routines, such as printing or math functions.

Linking methodWhere the library code comes from at run time
Static linkingCode included in the executable during the build
Dynamic linkingA compatible shared library loaded at startup or during execution

The resulting executable is built for a target platform, including an ISA and operating-system conventions. The user does not need the ahead-of-time compiler to run it. Shared libraries or runtime components can still be required.

Understand what a successful build proves

In Java, int count = "five"; fails a type check before execution. Text cannot initialize that integer variable. The program must pass the required compile-time checks before a runnable build is available.

Other faults survive compilation. Division by a user-entered zero depends on runtime data. A program intended to subtract that instead adds can run and return the wrong value. Compilation checks language rules; testing must also check that the result matches the intended task.

Key Takeaways

  • Compilation translates source into target code, which need not be machine code.
  • Compiler stages check tokens, structure, and meaning before producing target instructions.
  • Optimization preserves required behavior; constant folding evaluates constants during the build.
  • A linker connects object files and library code.
  • A successful build can still contain runtime failures or logic errors.

1.3 Interpretation, bytecode, and JIT compilation

A program does not have to become a native executable before a user starts it. An interpreter carries out the program through another running program. Many implementations combine interpretation with compilation, so the useful question is how this particular system executes code.

Execute through an interpreter

An interpreter needs to be available whenever it runs the program. An interactive REPL repeats four actions: read an entry, evaluate it, print a result, and loop for the next entry. It gives quick feedback while trying small expressions.

Interpreting does not necessarily mean reading raw source one line at a time. CPython, the standard Python implementation, first compiles a file into bytecode. A software virtual machine then executes those internal instructions. Bytecode belongs to that runtime; it is not the processor's native instruction set.

Why some errors appear earlier

Compare two Python files. This one has valid syntax:

print("start")
x = 10
print(x + "5")

It prints "start", binds x to 10, and then raises TypeError. Adding that integer to a string fails only when the expression is executed. A similar operation in an unvisited branch can remain unnoticed until the branch runs.

Now remove the closing parenthesis from the last line. CPython cannot compile the file because its syntax is incomplete. It reports a syntax error before any statement in that file executes, so "start" is not printed. Syntax checking of the whole file and runtime checking of an executed operation happen at different times.

Share bytecode, specialize while running

Java shows a common hybrid route. javac Hello.java produces Hello.class, which contains bytecode. java Hello starts a compatible Java Virtual Machine (JVM). Different JVM implementations let that bytecode run on different processor families. The bytecode is portable across compatible runtimes; each runtime must itself work on its host platform. C# similarly targets an intermediate language used by .NET.

A just-in-time (JIT) compiler generates machine code while the program runs. Java's HotSpot runtime commonly starts with interpretation and compiles frequently used methods. Browser JavaScript engines also combine execution techniques, including JIT compilation. Compiling busy parts can improve sustained speed, but uses time and memory during execution.

RouteUseful advantageCost to consider
Ahead-of-time native buildWork is translated before launch; no compiler needed by the userSeparate compatible builds for target platforms
InterpretationQuick edit-and-run feedback with an installed interpreterRepeated runtime work can slow compute-heavy code
Bytecode with a VM and JITPortable intermediate code with runtime optimizationRuntime installation, warmup, and memory overhead

A loop adding one billion values typically runs much faster in compiled C than in CPython: CPython performs interpreter work and type checks around many operations. For a script renaming 20 files, development effort may matter far more than that difference. Measure the workload before treating a language label as a speed prediction.

Compiled and interpreted describe implementation choices. CPython uses bytecode interpretation; PyPy uses JIT techniques. A language can have more than one implementation, and one implementation can use several execution methods.

Key Takeaways

  • An interpreter executes a program and must be present to run it.
  • CPython compiles a file to bytecode before executing it.
  • A syntax error can prevent a file from starting; a runtime error appears when its operation executes.
  • A JIT compiler creates native code during execution, often for frequently used parts.
  • Execution methods belong to implementations, and performance depends on the workload.
2

Read what short programs do

Reading code involves two questions: whether its form follows the language rules and what its operations mean. The same-looking expression can behave differently in two languages. Learn to classify errors, then trace assignments, decisions, loops, and function calls in execution order. A small table of changing values makes the difference between what a programmer intended and what the program actually does visible.

2.1 Syntax, meaning, and three kinds of error

Syntax sets the rules for valid program text. Semantics gives that text its meaning. A program can follow every grammar rule and still calculate the wrong result, so checking its appearance is only the first part of reading it.

Recognize the pieces and the grammar

Tokens include keywords such as if, identifiers such as total, literals such as 3 or "hello", operators such as + and ==, and punctuation. A grammar describes how these pieces can form expressions and statements.

Python requires a colon after if x > 3: and uses indentation to mark the statements controlled by that condition. C, Java, and JavaScript use curly braces for blocks; C and Java also require semicolons after many statements. In those languages, indentation helps a human reader but does not select the block.

Different syntax can express a similar action. These statements print a greeting:

print("hello")
printf("hello\n");
System.out.println("hello");

The C escape \n adds a newline. Knowing the idea of printing helps when learning each language's way of expressing it.

The same symbols can mean different things
Expression and languageResultRule
7 / 2 in Python or JavaScript3.5Division produces a fractional result
7 / 2 with integer operands in C or Java3Integer division truncates toward zero
-7 // 2 in Python-4Floor division rounds down
-7 / 2 with integers in C-3Truncation moves toward zero
"3" * 4 in Python"3333"A string is repeated
"3" * 4 in JavaScript12Text is converted to a number for multiplication

A symbol's familiar appearance does not determine its meaning across languages.

Evaluation order matters too. Precedence makes multiplication and division happen before addition. With a = 4 and b = 6, average = a + b / 2 divides 6 by 2 first, then adds 4, producing 7.0. Parentheses in average = (a + b) / 2 make the sum happen first and give 5.0.

Tell errors apart

A syntax error breaks the grammar, such as a missing Python colon. A runtime error occurs during an executed operation, such as Python division by zero. A logic error allows execution but gives an unintended answer, as in the incorrect average.

Some valid expressions deliberately skip an operation. In Python, x != 0 and 10 / x > 2 uses short-circuit evaluation. When x is zero, the first condition is false, so the right side is not evaluated and no division occurs. Reversing these conditions would remove that protection. Python writes and, or, and not; C, Java, and JavaScript use &&, ||, and !.

Assignment and comparison are another important distinction. = stores a value; == tests equality. C permits if (x = 5): it assigns 5 and treats that nonzero value as true. Python rejects if x = 5: as invalid syntax. The same mistake can therefore be caught at a different stage in different languages.

Key Takeaways

  • Syntax defines valid form; semantics defines meaning.
  • Operators can have different meanings in different languages.
  • Precedence and parentheses determine which operations are grouped.
  • Syntax, runtime, and logic errors require different kinds of checks.
  • Short-circuit evaluation can skip an unsafe operation.
  • Assignment stores a value; equality comparison tests values.

2.2 Trace values through control flow and calls

A short program is easier to understand when you follow one executed statement at a time. Keep the current values visible instead of jumping from the first line to a guessed result.

Expressions and assignments

An expression produces a value: price * 3, x > 10, and len(name) are examples. A statement performs an action, such as assigning a value or controlling a block. In total = price * 3, the right-hand expression is evaluated before its result is assigned to total.

A variable is a name that refers to a value. Assignment gives the name its current value; it is not a mathematical claim that two sides have always been equal. If count holds 4, count = count + 1 first reads 4, adds 1, and binds count to 5.

Follow the next executed statement

Three structures organize control flow:

  • Sequence runs statements in order.
  • Selection uses a condition to choose a path, such as if and else.
  • Iteration repeats a block with a for or while loop.

The structured program theorem, associated with Böhm and Jacopini in 1966, shows that these structures can express any algorithm. A for loop commonly visits the items of a collection. A while loop repeats while its condition remains true.

Consider this Python loop:

total = 0
for n in [3, 8, 5]:
    total = total + n

The indented assignment runs once for each item. A trace table records the state after each update:

MomentCurrent ntotal
Before the loopNo item yet0
First update33
Second update811
Third update516

Each new total uses the previous one. Resetting total inside the loop would produce a different calculation because earlier additions would be lost.

Enter and leave a function

A function groups a task under a name. A parameter is a name in the definition; an argument is a value supplied by a call:

def area(width, height):
    return width * height

result = area(3, 4)

The call binds width to 3 and height to 4. The multiplication produces 12, and return sends it to the caller. Execution continues at the assignment, so result receives 12. The names describe one task that can be reused with other arguments.

A name has a scope

Scope determines where a name can be used. Names created inside a function are ordinarily local to that call. A top-level name is global to its module. Separate calls have separate local bindings even when they use identical names.

count = 7

def reset():
    count = 0

reset()
print(count)

This prints 7. The assignment inside reset creates a local count; it does not replace the global binding. A function can instead return a result and let its caller assign that result.

Scope and lifetime answer different questions. A local name is not available to outside code just because the object it referred to still exists. Returned objects can remain alive, and a returned nested function can keep access to variables from its enclosing call; that retained environment is part of a closure.

Key Takeaways

  • Evaluate the right-hand expression before updating an assignment target.
  • Trace sequence, selection, and iteration in the order actually executed.
  • Parameters are definition names; arguments are supplied values.
  • A return value passes a result back to the caller.
  • Local and global names can have separate bindings; scope differs from lifetime.
3

Types, references, and memory

Values need a type, a place in memory, and a lifetime. These choices explain why text that looks numeric may reject arithmetic, why two names can expose the same changed list, and why unused data sometimes remains allocated. Compare the checks languages perform, follow a function call through memory, and examine three ways to release storage. Each approach changes the balance of control, convenience, and safety.

3.1 Types decide which operations make sense

A data type specifies possible values and the operations supported on them. An integer supports whole-number arithmetic; a Boolean represents true or false. Strings, lists, records, and objects combine or organize other values. Types also guide how values are represented in memory, although a value's size may depend on the value and implementation.

When are types checked?
ApproachWhen checks happenExample
Static typingBefore executionJava rejects int count = "five";
Dynamic typingDuring executionPython checks whether an executed operation fits its operands

In Python, count = 5 followed by count = "five" is valid. The name now refers to a string. Evaluating count + 1 then raises TypeError. Dynamic typing gives values definite types; it does not remove types.

Static typing does not require a visible type label on every variable. Type inference lets a compiler derive a type from surrounding code. Kotlin infers an integer for val n = 5, and Rust can infer an integer for let n = 5;. Python's optional type hints are different: separate tools such as mypy can check them, but ordinary Python execution does not enforce them.

When are values converted?

Checking time and conversion strictness are separate questions. Python rejects "3" + 4; explicit conversion in int("3") + 4 gives 7. JavaScript applies different automatic conversion rules for different operators:

  • "3" + 4 gives the string "34".
  • "3" * 4 gives the number 12.
  • "3" - 4 gives the number -1.

Automatic conversion is called coercion. The labels "strong" and "weak" typing are informal ways to discuss conversion strictness, not a second name for static and dynamic typing. Python is dynamic and strict about the mixed addition above; C is static and still permits many implicit numeric conversions.

Python's input() returns a string. If a person types "30", the result still has type str. age + 1 fails, while int(age) + 1 gives 31 when age holds that text. What appears on screen is not enough to determine the type.

Numbers have representation limits

A signed 32-bit integer represents values from −2,147,483,648 through 2,147,483,647. Java's int wraps from the maximum to the minimum when 1 is added. C signed overflow is undefined behavior, so that same wraparound must not be assumed in C. Python integers grow as memory permits: 2 ** 100 is exactly 1,267,650,600,228,229,401,496,703,205,376.

Floating-point numbers represent most decimal fractions approximately in binary. In Python and JavaScript, 0.1 + 0.2 produces 0.30000000000000004. Comparing that result directly with 0.3 gives false.

When a calculation allows a small error, compare values using a tolerance chosen for the task, for example abs(a - b) < 1e-9. An absolute tolerance this small suits only some scales. For exact decimal accounting, use whole smallest units or a suitable decimal type. Changing the comparison cannot make an approximate representation exact.

Key Takeaways

  • A type defines values and supported operations.
  • Static and dynamic typing describe checking time, while coercion concerns conversion rules.
  • Type inference supports static typing; Python hints need separate checking tools.
  • Fixed-width integers can overflow, with language-specific behavior.
  • Floating-point rounding affects equality tests and the choice of number representation.

3.2 Follow names, objects, and function calls in memory

Main memory stores a running program's data. Each byte has an address. Languages provide names so that programmers can work with values without writing raw addresses for every operation.

Calls use frames; objects have lifetimes

In C, an int commonly occupies 4 bytes, and &x obtains the address of x. Higher-level languages often hide addresses behind references. The important distinction is between a binding that names an object and the object itself.

The call stack tracks active function calls. Each call has a stack frame with information such as parameters, local bindings, and where execution should continue after return. If main calls area(3, 4), an area frame is added above the caller's frame. The function calculates 12, returns, and its frame is removed. For ordinary nested calls, the newest call returns first.

Recursion adds a call for each unfinished level. If calls keep accumulating, available stack space can run out. Python normally raises RecursionError after roughly 1,000 nested calls, with the limit depending on its settings. A stopping condition must be reached for a finite recursive calculation to finish.

The heap holds dynamically allocated objects, such as a list that grows or an object that outlives the function that created it. Allocation reserves storage. A returned list can remain on the heap after its creating call has ended.

Copying a reference shares an object

A reference identifies an object. A pointer in C or C++ explicitly represents an address used to access data. Copying a reference copies access to the object, not all the object's data.

a = [1, 2]
b = a
b.append(3)

After the first line there is one list. The second line binds another name to it. The last line mutates that list, so both a and b show [1, 2, 3]. This sharing is aliasing. Using b = a.copy() would create a separate outer list; for this list of integers, appending to the new list would leave a unchanged.

Contrast assignment with mutation:

x = 5
y = x
y = y + 1

The integer 5 is immutable. Addition produces 6, and only y is rebound. x still refers to 5. Python numbers, strings, and tuples are immutable; lists and dictionaries are mutable. A tuple's fixed contents can include a reference to a mutable list, so immutability of the outer container does not freeze every nested object.

Apply the same rules to arguments

When Python passes a list to a function, the parameter refers to the same object. Calling items.append(4) inside the function changes that list. Assigning items = [] binds only the local parameter name to a new list. The caller's name remains bound to the original object.

C passes argument values by value. To let a function update a caller's variable through its address, pass a pointer to it. In each language, ask two separate questions while tracing: "Which object does this name refer to?" and "Does this operation change that object or bind a name to something else?"

Key Takeaways

  • The call stack tracks unfinished calls; the heap holds dynamically allocated objects.
  • An object can outlive the function that created it.
  • Copying a reference shares an object; it does not duplicate its data.
  • Mutation changes an object, while rebinding changes what a name refers to.
  • Argument passing follows the same distinction between shared objects and local bindings.

3.3 Release memory without losing live data

Allocated memory must eventually become reusable. The challenge is releasing data after its last use while keeping every object the program still needs. Languages assign this responsibility to the programmer, a runtime system, or compiler-checked ownership rules.

Manual management and its failures

In C, malloc(n) requests n bytes and returns a pointer to the block if successful. free(p) releases a block obtained by allocation. The programmer must use it at the right time and exactly once. C++ also supports manual management, although its standard tools can automate release through object lifetimes.

Several mistakes have different causes:

FaultWhat went wrong
Memory leakUnneeded allocated storage is not released
Use after freeA pointer is used after its target storage was released
Double freeThe same allocated block is released twice
Buffer overflowA write goes beyond the bounds of a memory block

A pointer left referring to released storage is dangling. The storage might be reused for something else, so accessing it can corrupt data or crash the program.

For a C array int scores[10], valid positions are 0 through 9. Writing to scores[10] goes beyond the array, and C provides no automatic bounds check. Python raises IndexError when index 10 is used on a ten-item list, preventing that access from overwriting adjacent memory.

Trace reachability or count references

A tracing garbage collector starts from live roots, such as active references, follows links between objects, and can reclaim unreachable objects. Java, C#, Go, and JavaScript use tracing approaches. Collection requires runtime work and memory; depending on the collector, it can introduce pauses.

Reference counting tracks references to an object. For a fresh Python list, the simplified sequence is: a = [1, 2] supplies one reference; b = a supplies a second; del a removes one; b = None removes the other. CPython normally reclaims this ordinary list when its count reaches zero.

A group of objects can keep one another's counts above zero even when no live part of the program can reach the group. This is a reference cycle. CPython supplements reference counting with cycle collection. Reference counting and tracing are both automatic memory-management techniques.

Check ownership before execution

Rust gives each value an owner and checks rules for borrowing references. When ownership ends, generated cleanup code releases the value. Safe Rust rejects uses of references that could outlive their valid target, without requiring a tracing garbage collector. Swift uses automatic reference counting inserted by the compiler.

Memory safety matters beyond crashes. Microsoft reported in 2019 that roughly 70% of the vulnerabilities it assigned CVEs to each year involved memory safety; Chromium reported a similar share of serious bugs in 2020. In December 2023, CISA, NSA, FBI, and international partners urged software makers to plan transitions toward memory-safe languages.

Automatic reclamation still retains reachable objects. An ever-growing global list can keep every old record alive even after the application has finished using it. A collector cannot decide that these reachable records have become unnecessary to the task; the program must stop retaining them.

Key Takeaways

  • Manual allocation requires correctly timed release.
  • Leaks, use after free, double free, and buffer overflow have distinct causes.
  • Tracing finds unreachable objects; reference counting reacts to lost references.
  • CPython adds cycle collection to reference counting.
  • Ownership can arrange release without a tracing collector.
  • Automatic collection cannot reclaim objects that the program still retains.
4

Organize changing state and cooperating objects

One way to describe a program is as commands that update values. Another groups those values with the operations that control them. These views often work together: an object method can contain an ordinary loop. Trace changing state first, then build a small account model to see how classes protect rules and how inheritance, polymorphism, and composition let related objects share an interface without taking on the wrong responsibilities.

4.1 Commands, procedures, and shared state

A programming paradigm is a way to organize code and think about computation. In imperative programming, a program gives commands that change its state: the values currently held by variables and objects.

Make the state changes visible

A running total is a simple imperative calculation:

total = 0
for n in [3, 8, 5]:
    total = total + n

The initial state is total = 0. Adding 3 changes it to 3. Adding 8 changes it to 11. Adding 5 changes it to 16. Each assignment uses the state left by the previous step, so order is part of the method.

This style matches the processor's step-by-step changes to registers and memory. It also fits algorithms that deliberately update a collection in place, such as a sorting routine that exchanges items.

Give tasks names

Procedural programming organizes imperative code into functions or procedures. A report program might call read_scores(), average(scores), and print_report(avg). The names let a reader see the overall task without following every internal line at once. Each procedure can be understood and tested separately.

C, Pascal, Fortran, and COBOL are classic procedural languages. Python also supports this style: a script can use named functions without defining any classes.

Structured programming organizes control flow with sequence, selection, loops, and procedure calls. It avoids arbitrary jumps that make the next executed statement hard to predict. Edsger Dijkstra's 1968 letter "Go To Statement Considered Harmful" helped popularize this approach. Controlled exits such as break, continue, and return remain useful: they leave a defined loop iteration, loop, or function.

Effects can extend beyond a return value

A side effect changes something outside a function's returned result. Printing, saving a file, sending a network message, and changing shared data are side effects. Programs need such actions to interact with the world.

Problems arise when unrelated code silently changes the same state. Suppose apply_discount() reduces a global price by 10%, and add_shipping() adds 5. Starting from 100, discounting first gives 90, then shipping gives 95. Shipping first gives 105, then discounting gives 94.5. Both procedures perform their own steps correctly, but the combined result depends on their order.

Passing values and returning new results can make that order visible:

price = add_shipping(apply_discount(100))

The inner call runs first: discount, then shipping. A different nesting expresses a different rule. Clear data flow helps reveal whether shipping should itself receive the discount.

OrganizationMain question while reading
Imperative commandsWhich value changes next?
Named proceduresWhich task does this call perform?
Shared global updatesWhich earlier action changed this value?

Imperative code is still common inside modern object methods and event handlers. Choosing a broader object-oriented or functional design does not prevent a small part from using direct commands where those steps make the algorithm clear.

Key Takeaways

  • Imperative code describes steps that change stored state.
  • Procedures give coherent tasks names and boundaries.
  • Structured control flow makes execution paths easier to follow.
  • Side effects include changes to shared data and interactions with the outside world.
  • Shared updates can make results depend on call order.

4.2 Classes keep data with its rules

Object-oriented programming (OOP) models a program as cooperating objects. Each object combines data with operations on that data. An account can hold its owner and balance while offering deposit and withdrawal operations.

Separate the blueprint from its instances

A class defines the attributes and methods its objects provide. An object, or instance, is one particular member of that class. BankAccount is the blueprint; Ana's account with 150 and Ben's account with 40 are distinct instances. Their balances can change independently.

An attribute stores data associated with an object. A method is an operation called through an object, such as acct.deposit(50). self in a Python instance method refers to the object receiving the call.

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("amount must be positive")
        self._balance = self._balance + amount

Calling BankAccount("Ana", 100) constructs a new instance. Its __init__ method initializes the instance's data. A constructor is the setup process for a new object; Python separates creation from this initialization step. Calling acct.deposit(50) on that account changes its balance from 100 to 150.

Put the rule at the update point

Encapsulation brings data and the operations that maintain its rules together. Suppose an account must never have a negative balance. The withdrawal method checks the proposed change before making it:

def withdraw(self, amount):
    if amount <= 0:
        raise ValueError("amount must be positive")
    if amount > self._balance:
        raise ValueError("insufficient funds")
    self._balance = self._balance - amount

This method belongs inside BankAccount. From a balance of 150, withdraw(200) raises an error and leaves 150. withdraw(30) succeeds and leaves 120. The failed call does not first subtract and then discover the problem; checking happens before the update.

Other code should use those methods instead of assigning an unchecked balance. Java and C# can restrict direct access with private. Python's leading underscore in _balance signals an internal detail by convention; it does not enforce privacy.

Information hiding goes further: callers depend on the operations a class offers without needing to know its internal representation. The balance could later be stored differently while the public deposit and withdrawal operations retain their meaning.

Let the problem suggest responsibilities

A library system has books, members, and loans. Those nouns suggest possible classes or attributes; actions such as borrowing and renewing suggest methods. Choose one clear responsibility for each class. An account class that also sends every email and formats every report becomes harder to change because unrelated work is mixed together.

Classes and objects appeared in Simula 67 in 1967; Smalltalk helped spread the approach in the 1970s. Java, C#, C++, and Python support OOP, but using one of those languages does not force every program into classes. The useful design question is which data and rules belong together.

Key Takeaways

  • A class defines attributes and methods; each instance has its own instance data.
  • Methods act on the object receiving the call.
  • Encapsulation puts validation and state changes together.
  • Python underscores signal internal attributes by convention; some languages enforce private access.
  • Information hiding lets callers depend on the public operations instead of its internals.
  • A class should have a coherent responsibility.

4.3 Specialize behavior and combine objects

Objects often need to share operations while differing in details. Inheritance, polymorphism, and composition solve different parts of that design problem.

Specialize an existing kind of object

Inheritance defines a subclass from a superclass. In Python, class SavingsAccount(BankAccount): gives the specialized class access to inherited account behavior. It can add add_interest(rate): a balance of 1000 with a 2% interest addition becomes 1020.

The intended relationship is is-a. A savings account is still a bank account and must honor the rules expected of bank accounts. Reusing a few lines is not enough reason to claim that one kind of object is another.

A subclass can override a method with its own implementation. Suppose a checking account charges a fee of 1 for each successful withdrawal. With a balance of 100, withdrawing 30 costs 31 and leaves 69. To preserve the account rule, the method must check the withdrawal plus the fee before changing the balance. If that combined cost exceeds the balance, it refuses the whole operation.

Make one call work with different objects

Polymorphism lets the same operation work across different object types. A circle and rectangle can both provide area():

for shape in shapes:
    print(shape.area())

The loop does not need an if for every shape class. Each object supplies its own implementation. Selecting that implementation during execution is dynamic dispatch.

For Circle(2), the area is about 12.57 square units. For Rectangle(3, 4), it is 12 square units. Their combined area is about 24.57 square units. A later Triangle can join the collection by providing a compatible area() operation; the loop itself does not need another branch.

An interface states the operations a type must offer. Java's interface Shape { double area(); } requires implementing classes to supply the area method. An abstract class can provide some shared implementation while leaving other methods for subclasses. Python often uses duck typing: an object can work in the loop if it provides the required operation, even without a shared superclass.

Hold a part instead of becoming it

Composition stores other objects as parts. A car has an engine and wheels. It can call its engine's start() method without inheriting from Engine.

RelationshipSuitable designReason
Savings account is an accountInheritanceIt preserves the account's expected behavior
Car has an engineCompositionThe engine is a component, not the car's category
Different shapes offer areaShared operation or interfaceThe caller can request the operation uniformly

Deep inheritance chains can spread the effects of a superclass change through many classes. A subclass that must disable inherited behavior can signal a poor is-a relationship. Composition often keeps such dependencies smaller and clearer.

The four commonly named OOP pillars fit together here: encapsulation protects data through operations, abstraction hides implementation details, inheritance specializes a type, and polymorphism lets a caller use a shared operation across implementations.

Key Takeaways

  • Inheritance models an is-a relationship and must preserve expected behavior.
  • Overriding replaces a method implementation in a specialized class.
  • Polymorphism lets one operation invoke different implementations.
  • An interface states required operations; duck typing relies on the operations an object provides.
  • Composition models objects that have other objects as parts.
5

Compute new values and describe wanted results

A program can focus on transformations of values or on the result an engine should produce. Pure functions make dependencies explicit, while immutable data avoids changes through shared references. Declarative queries let a specialized engine choose execution steps. Follow a data pipeline and a database query to see what these approaches make easier, and where input, output, and concrete machine instructions still enter the picture.

5.1 Pure functions and data pipelines

Functional programming builds results by combining functions. Its central habit is to produce new values from inputs while limiting shared mutation. Lisp introduced widely used functional ideas around 1958; Haskell, Erlang, Clojure, F#, and OCaml develop those ideas in different ways.

Know what makes a function pure

A pure function gives the same result for the same arguments and has no side effects. def with_tax(price): return price * 1.2 illustrates the idea: with_tax(50) gives 60.0. A function that also appends to a global list or prints a message has an effect beyond its returned value. A result that depends on the current time also depends on more than the arguments.

A pure call can be replaced by its returned value without changing program behavior. This property is referential transparency. It makes isolated tests straightforward and permits independent calls to run in parallel without competing to update shared state.

Immutability keeps an existing value unchanged. [n + 1 for n in nums] creates a new list, leaving nums intact. A program can still represent changing information by producing new versions; it does not have to overwrite the old version.

Pass behavior as data

Functions are first-class when they can be assigned to names, passed as arguments, and returned as values. A higher-order function takes a function or returns one. A lambda is an unnamed function expression, such as lambda n: n * n.

Three common higher-order operations form a pipeline:

  • filter keeps items that satisfy a test.
  • map transforms each remaining item.
  • reduce combines items into one result using an accumulator.

To total the squares of even numbers from 1 through 6, filtering leaves 2, 4, and 6; squaring gives 4, 16, and 36; summing gives 56.

sum(map(lambda n: n * n,
        filter(lambda n: n % 2 == 0, range(1, 7))))

Here % 2 == 0 tests evenness, and range(1, 7) supplies 1 through 6. Haskell expresses the same calculation as sum (map (^2) (filter even [1..6])).

With reduce imported from Python's functools, reduce(lambda acc, n: acc + n, [3, 8, 5], 0) combines 0 with 3 to get 3, then with 8 to get 11, then with 5 to get 16. The pipeline describes transformations without a surrounding loop updating a shared total.

Preserve an environment or call a smaller problem

def multiplier(k): return lambda x: k * x returns a function that retains access to k. Thus multiplier(3)(10) returns 30 even after the outer call has finished. The returned function and retained environment form a closure.

Recursion has a function call itself on a smaller problem. Factorial returns 1 at n == 0; otherwise it returns n * factorial(n - 1). For 5, the products give 120. A finite calculation needs a reachable base case. Python's unresolved nested calls eventually raise RecursionError; languages with proper tail calls can reuse stack space for calls in final position.

Using functions alone does not make code functional: procedural functions can mutate state. Real functional programs still read input and write output, commonly keeping effects near the program's edges and the central calculations pure.

Key Takeaways

  • Pure functions depend only on their arguments and have no side effects.
  • Immutable updates create new values.
  • First-class functions can be passed and returned; higher-order functions take or return functions.
  • Filter selects, map transforms, and reduce combines.
  • A closure retains its creation environment.
  • A finite recursive calculation needs a base case it actually reaches.

5.2 State a result and let an engine find it

Declarative programming describes the result wanted while leaving execution steps to a language engine. Functional code is commonly grouped under this broad idea, alongside query languages and logic programming.

Read a query as a request

A relational database stores data in tables. Suppose students has the name and grade rows Ana 92, Ben 75, Chen 80, and Dara 64:

SELECT name
FROM students
WHERE grade >= 80
ORDER BY name;

FROM names the table, WHERE gives the condition, SELECT chooses the returned field, and ORDER BY requests alphabetical order. Ana and Chen satisfy the condition, including Chen's grade of exactly 80. The result is their names in that order.

An imperative program would create a result list, visit rows, test each grade, append qualifying names, and sort the list. SQL gives the database the required outcome without writing that loop.

A query optimizer chooses an execution plan. It might scan the table or use an available index, a lookup structure that can speed suitable searches. Many indexes use sorted structures; others use different arrangements. Adding an index can change the plan without changing the query. More data still means work: declaring the result does not guarantee that every query is cheap.

Ask what follows from facts and rules

Logic programming describes facts and relationships, then searches for values satisfying a question. In Prolog:

parent(tom, bob).
parent(bob, ann).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).

The rule means X is a grandparent of Z when there is a Y for which both parent relationships hold. The query ?- grandparent(tom, Who). finds Who = ann: Tom is Bob's parent and Bob is Ann's parent. Capitalized names stand for variables; the lowercase names identify particular people.

Recognize declarative tools around you

A spreadsheet formula =SUM(B2:B5) states a dependency. The spreadsheet recalculates it when relevant cells change. The regular expression [0-9]{3}-[0-9]{4} describes three digits, a hyphen, and four digits, as in "555-0199". A text-matching engine performs the search.

CSS states appearance with a rule such as h1 { color: navy; }; HTML marks page structure. They are declarative notations, commonly classified as styling and markup languages. Their role differs from a general-purpose language that can express a wide range of computations.

Language kindTask focusExamples
Domain-specific languageA particular kind of workSQL, regular expressions, CSS
General-purpose languageMany kinds of programsPython, Java, Haskell

Declarative code can stay close to a problem statement and let an engine improve its method. It also gives the programmer less direct control over individual steps, which can make a slow plan harder to diagnose. Concrete instructions still run underneath: declarative describes the programmer's expression of the task, not a processor that works without steps.

Key Takeaways

  • Declarative code states a wanted result; an engine chooses execution steps.
  • SQL selects rows and fields through conditions and requested ordering.
  • An optimizer can change a query plan without changing the query.
  • Logic programming derives answers from facts and rules.
  • Domain-specific languages target particular tasks; general-purpose languages support many kinds of programs.
6

Choose a style and a language for the job

Different program styles can produce the same answer while organizing the work differently. Comparing them on one task makes their trade-offs easier to see. A language choice then adds practical constraints: the target platform, available libraries, memory and speed limits, safety needs, and the people maintaining the code. A project with several parts can use a different language and a different mix of styles for each part.

6.1 One task in several programming styles

A paradigm changes how a solution is expressed, not which numerical answer a correct solution should produce. Consider orders with amounts [120, 45, 300, 80]: find the total of amounts strictly greater than 100.

Follow state changes

The imperative version exposes the loop and accumulator:

total = 0
for amount in amounts:
    if amount > 100:
        total = total + amount

The first amount changes total to 120. The second leaves it unchanged. The third changes it to 420, and the fourth leaves it there. The program makes the test and update order visible.

Transform a collection

A compact functional-style Python expression is sum(a for a in amounts if a > 100). It selects 120 and 300 and combines them into 420. Another version is sum(filter(lambda a: a > 100, amounts)). Here the predicate describes membership and sum combines the selected values.

A generator expression or filter call uses Python's multi-paradigm features; its presence does not make every surrounding part of the application pure.

Put rules with objects

In an object-oriented design, an Order stores an amount and offers is_large(), which returns self.amount > 100. An OrderBook holds order objects and offers large_total(). That method can loop through orders, ask each whether it qualifies, and add qualifying amounts.

The total remains 420. The difference is where responsibilities live: the rule for a large order belongs to Order, and collection-level work belongs to OrderBook. Other features can reuse the rule without copying its threshold test.

Ask the database

If the data is already in a relational database, SQL can request the total:

SELECT SUM(amount)
FROM orders
WHERE amount > 100;

The database chooses the retrieval and aggregation plan. The query does not instruct a caller to visit every row or keep a local accumulator.

StyleWhat the example emphasizes
ImperativeOrdered tests and updates to a total
Object-orientedOrders and collection objects with defined responsibilities
FunctionalSelection and combination of values
Declarative SQLThe required result over stored data
Combine styles where they help

Python supports procedural, object-oriented, and functional techniques. JavaScript provides objects, first-class functions, and closures. C++, C#, Kotlin, Scala, Swift, and Rust also mix styles. Java is strongly associated with OOP but has supported lambdas since Java 8 in 2014; Haskell is purely functional.

A web application can use classes for users and orders, functional transformations for report data, imperative updates inside methods, and SQL for database queries. These choices work at different layers.

Event-driven programming adds another organizing idea: register a handler that runs when an event arrives. In JavaScript, button.addEventListener("click", showMenu) passes showMenu as a function value. A later click invokes it. The handler can itself use objects, pure transformations, or imperative steps. Its event-driven role says when it runs, not the only style its body may use.

Key Takeaways

  • Correct solutions in different paradigms can compute the same result.
  • A style changes where steps, state, rules, and responsibilities appear.
  • Modern languages commonly support multiple paradigms.
  • An event handler runs in response to an event and can use other styles internally.

6.2 Choose from constraints and trade-offs

No programming language is the best fit for every project. General-purpose languages are commonly Turing complete: with unbounded time and memory, they can express the same class of computations. Real choices depend on whether a solution fits the actual platform, resources, tools, and people.

Start with what must work

The platform is often a hard constraint. Browsers run JavaScript and can run compiled modules through WebAssembly. TypeScript adds static checking and compiles to JavaScript. Swift is a standard choice for Apple-platform apps; Kotlin is recommended for Android. SQL is the usual language for relational queries.

Next consider the domain and ecosystem. Python has widely used data and machine-learning libraries such as NumPy and pandas. R focuses on statistics. C and C++ are common in operating systems, game engines, and embedded code. Java and C# are common in large business systems; Bash is useful for automating operating-system tasks.

These are practical associations, not exclusive territories. A suitable library can save far more development work than a small difference in language syntax.

Examine resource and safety needs

A microcontroller with only a few kilobytes of RAM may need fine control over memory and runtime overhead. C, C++, or Rust can be suitable where the target supports them. Demanding game or database components often favor those languages too, but engines and resource budgets differ: Unity uses C#, and some databases use Java or Go.

Before choosing for speed, find the bottleneck: the part limiting performance. A program waiting mainly for a network response will not become dramatically faster just because a small local loop is compiled more efficiently. Measure representative work on the target platform.

Memory safety reduces errors such as invalid memory access. Safe Rust checks ownership rules at compile time; managed languages such as Java, C#, Go, and Python provide runtime memory management. Static typing catches many incompatible operations before execution, which can help in a large codebase. Neither memory safety nor static typing proves that the program's logic is correct.

Include the cost of maintaining the result

Team knowledge, available developers, debuggers, test tools, libraries, licenses, and existing code all affect a decision. A familiar, supported language can reduce delivery and maintenance costs. Rewriting a working system creates migration work and risks even if the new language has attractive features.

For a city weather network, different components have different needs:

ComponentReasonable language choiceReason
Small sensor firmwareC or RustMemory control and supported hardware targets
Stored readingsSQLDatabase filtering and aggregation
Cleaning and analysisPythonData-processing libraries
Interactive browser displayJavaScript or TypeScriptBrowser support and interface tooling

A useful justification links each choice to a requirement: "The team already maintains the Java service and its libraries support the new feature." A popularity rank alone cannot supply that reasoning. Surveys can indicate a large community or hiring pool; they do not establish the best language for a particular workload.

Key Takeaways

  • Check platform constraints before preferences.
  • Libraries and tools connect a language to its problem domain.
  • Measure the bottleneck before choosing primarily for speed.
  • Memory safety and static checking reduce some faults, while logic still needs testing.
  • Team skills, existing code, and maintenance costs belong in the decision.
  • Different components can justify different languages.

Programming Languages and Paradigms examples

6 Walkthroughs

1

Follow a program from source to execution

13 steps
Track translation layers, inspect a build, and compare runtime execution routes.
  1. 1
    IntroductionOne calculation crosses several layers

    You write one calculation, but a processor needs small encoded instructions called machine code. A translator supplies the missing detail.

    Follow x = a + b downward from source, then follow the program forward through building and running. Keep two questions separate: what form is the code in, and which software is carrying it out? A compiler, an interpreter, and a JIT compiler answer the second one differently. That distinction will explain both portability and the timing of errors.

  2. 2
    ExampleOne calculation becomes several actions

    For x = a + b, imagine four lower-level actions:

    1. Load the value of a into a register.
    2. Load the value of b into another register.
    3. Add the two register values.
    4. Store the result for x.

    The high-level statement hides register selection and storage details. Actual generated instructions can differ because a compiler may optimize the work. The required calculation stays the same.

  3. 3
    ExplanationReadable instructions still target a processor

    Assembly uses instruction names such as mov and add. An assembler encodes them as machine instructions.

    For x86-64, mov eax, 1 puts 1 in EAX; add eax, ebx adds EBX to it. Those registers and encodings belong to that instruction set architecture. Replacing bytes with readable names helps a person, but does not turn the instructions into a portable high-level description.

  4. 4
    ChallengeMove the program to another processor

    A team has portable C source and an x86-64 native executable. It needs an ARM native executable. Which artifact can it use as the starting point, and what work is needed?

    ---reveal---

    Use the source and compile it for the ARM target, with the required platform support. The x86-64 executable contains instructions for a different ISA. Source portability allows another translation; it does not make those existing machine instructions native ARM code.

  5. 5
    ExampleIdentify tokens before structure

    Consider total = price * 3; in C.

    Lexical analysis finds six tokens: total, =, price, *, 3, and ;. Parsing then groups price * 3 as the expression whose result is assigned to total.

    These are distinct jobs. Tokenizing identifies the pieces; parsing establishes how they fit. Semantic analysis can then check questions such as whether the names are declared and the operand types are compatible.

  6. 6
    ExampleMove constant work into the build

    The source expression 60 * 60 * 24 uses only constants. Constant folding can evaluate it before execution: first 3600, then 86400.

    Code generation can use that result instead of instructions that repeat the multiplications. This is an optimization because it changes how the work is performed while preserving the required result. Replacing a value with a merely convenient approximation would not be justified by this rule.

  7. 7
    ExplanationConnect translated files to library routines

    Compilation can produce separate object files. A linker connects those files and resolves references to library operations.

    Static linking includes library code in the built executable. Dynamic linking relies on a compatible shared library loaded later. Neither approach requires the user's machine to have the ahead-of-time compiler, but a dynamically linked executable still needs its runtime dependencies.

  8. 8
    ChallengeA successful build gives the wrong answer

    A C program builds successfully. Its report should subtract a used quantity from a starting quantity, but the code adds them. Has the compiler failed to check a language rule?

    ---reveal---

    No. Addition can be valid syntax with compatible types while expressing the wrong task. This is a logic error. The compiler checks language rules and generates code; a test with a known expected result is needed to expose the mismatch between the calculation and its intended purpose.

  9. 9
    ExplanationA runtime can execute intermediate instructions

    CPython compiles a valid Python file into bytecode, then executes that bytecode in its virtual machine. The processor executes the runtime's machine instructions while the runtime carries out the Python operations.

    Java also uses bytecode: javac produces class files for a compatible JVM. Bytecode belongs to a runtime instruction model, so it should not be mistaken for a universal processor instruction set.

  10. 10
    ExampleCompare two moments when execution can stop

    In this valid Python file, the first line runs:

    print("start")
    x = 10
    print(x + "5")
    

    It prints "start" before the last line raises TypeError. Now remove the final closing parenthesis: CPython cannot compile the file, so no statement in that file runs. A syntax failure during preparation and a type failure during an executed operation stop the program at different stages.

  11. 11
    ExplanationCompile busy code during the run

    A JIT compiler produces machine code while the program runs. A runtime can interpret first, observe frequently used methods, and compile those methods.

    This shifts costs: compilation uses time and memory during the run, while repeated execution can become faster afterward. A long-running service may benefit more from that warmup than a tiny one-off script. The result depends on the implementation and workload.

  12. 12
    ChallengeSeparate bytecode portability from native speed

    A Java class file runs through a compatible JVM on two different ISAs. During each run, the runtime compiles a frequently used method. Why can one class file be shared while the generated machine code differs?

    ---reveal---

    The class file contains JVM bytecode, which both compatible runtimes understand. Each JIT targets its own host processor. Portability comes from the shared intermediate format and compatible runtimes; the machine instructions produced during execution remain platform-specific.

  13. 13
    RecapKeep levels and execution routes separate

    Machine code targets an ISA; assembly gives its instructions readable names; high-level source hides more hardware detail.

    A compiler translates code, an interpreter carries it out, and a JIT compiles during execution. A runtime can combine these methods. To explain a real system, identify its source, intermediate and native forms, then say which stage checks syntax and which stage executes the operation that may fail.

2

Trace meaning through a short program

10 steps
Read grammar, evaluation order, state changes, and function boundaries.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionForm and meaning answer different questions
  2. 2
    ExampleValid code can group the wrong calculation
  3. 3
    ExplanationThe first condition can protect the second
  4. 4
    ChallengePredict whether the division runs
  5. 5
    ExplanationAn assignment reads before it updates
  6. 6
    ExampleTrace each accumulator update
  7. 7
    ExampleFollow a call and its returned value
  8. 8
    ChallengeA local update can leave the caller unchanged
  9. 9
    ChallengeFollow grammar and state together
  10. 10
    RecapRead the executed path
3

Track types and shared objects

14 steps
Compare checking time, follow aliases, and decide when memory can be released.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionFollow values, objects, and storage
  2. 2
    ExplanationTyped values can move between names
  3. 3
    Key InsightInference and conversion answer different questions
  4. 4
    ChallengeRead digits as the value actually returned
  5. 5
    ExplanationRepresentation affects numeric results
  6. 6
    ExplanationA call frame can end before an object does
  7. 7
    ExampleTwo names point to one list
  8. 8
    ChallengeA parameter can mutate and then move away
  9. 9
    ExplanationIdentify exactly how storage is misused
  10. 10
    ExampleFollow references until the last one is gone
  11. 11
    Key InsightA cycle can have no live entry point
  12. 12
    ExplanationOwnership checks access before the run
  13. 13
    ChallengeFind why collection cannot reclaim a list
  14. 14
    RecapTrack three different decisions
4

Protect rules and choose object relationships

12 steps
Trace order-dependent commands, then use encapsulation, overriding, and composition.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionSteps need reliable boundaries
  2. 2
    ExampleOperation order changes shared state
  3. 3
    ExplanationMake a procedure boundary useful
  4. 4
    ChallengePredict a reordered update
  5. 5
    ExplanationGive each account its own state
  6. 6
    ExampleValidate before changing a balance
  7. 7
    ChallengeLocate a bypassed rule
  8. 8
    ExampleSpecialize without breaking the contract
  9. 9
    ExplanationLet the object choose its area method
  10. 10
    ConnectionHold the component that does the work
  11. 11
    ChallengeChoose two relationships in a new design
  12. 12
    RecapMatch boundaries to responsibilities
5

Transform a collection and query a result

13 steps
Follow a functional pipeline and a declarative query, with checks on effects and execution.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionState a transformation or a request
  2. 2
    ExplanationPurity describes more than the returned number
  3. 3
    ExampleSelect the even values first
  4. 4
    ExampleTransform and combine the selected values
  5. 5
    ChallengeApply the pipeline in a new case
  6. 6
    ExplanationReturn behavior that remembers its factor
  7. 7
    ExampleReduce the problem until a direct case applies
  8. 8
    ExampleRead SQL as a request over rows
  9. 9
    Key InsightKeep the result while changing the plan
  10. 10
    ExampleConnect two facts through a rule
  11. 11
    ChallengeRead a new query including its boundary
  12. 12
    ChallengeCombine a query with a pure transformation
  13. 13
    RecapKeep the input-to-result relationship visible
6

Compare styles and justify a language choice

10 steps
Keep one task fixed, then choose tools for components with different constraints.

Save this lesson to follow every Walkthrough step by step.

  1. 1
    IntroductionKeep the task fixed
  2. 2
    ExampleKeep the task fixed while changing the style
  3. 3
    ExamplePlace the same rule in objects or a query
  4. 4
    ChallengeDecide whether a design has one exclusive style
  5. 5
    ExplanationEliminate unsupported targets first
  6. 6
    ExampleChoose for a complete weather system
  7. 7
    TipMeasure what consumes the time
  8. 8
    ChallengeDefend a choice with competing constraints
  9. 9
    ChallengeConnect language choice to program organization
  10. 10
    RecapGive a reason tied to the requirement

Programming Languages and Paradigms practice questions

50 Practice Questions in 5 sets

Set 1: Read code and identify the execution route10 Practice Questions

Question 1 of 10

A developer replaces raw instruction bytes with names such as mov and jmp. At what language level is the program now written?
  1. Machine code
  2. Assembly language
  3. A high-level language
  4. A database query language
Show answerHide answer
Answer: B
Assembly writes processor-level operations with mnemonics and register names, making them easier for people to read. It remains tied to an instruction set architecture. Readable names alone do not make this a high-level language, because the programmer is still specifying individual low-level operations.

Question 2 of 10

During a build, a tool connects several object files and resolves a call to a library routine. What tool is performing this task?
  1. An assembler
  2. A parser
  3. An interpreter
  4. A linker
Show answerHide answer
Answer: D
A linker connects translated parts and resolves references to code defined elsewhere, including library routines. A compiler may have produced the object files earlier in the build. Parsing checks source structure; it does not connect the resulting files into a complete program.

Question 3 of 10

A Python file contains a first-line print statement and an unclosed parenthesis on its final line. What happens when CPython runs the file?
  1. It reports a syntax error before the print executes.
  2. It prints the first line and then reports a syntax error.
  3. It prints the first line and then raises a runtime error.
  4. It skips only the final line and runs the valid lines.
Show answerHide answer
Answer: A
CPython must compile the file before executing its statements, and the unclosed parenthesis prevents that preparation. The first print therefore never runs. Printing before failure would fit a runtime error in valid code, but this file has a syntax error that is found earlier.

Question 4 of 10

A valid Python program prints the wrong average because it adds one value after halving the other. What kind of error is this?
  1. A syntax error
  2. A runtime error
  3. A logic error
  4. A type mismatch error
Show answerHide answer
Answer: C
The operations complete, but their meaning does not match the intended average. That makes the fault a logic error. A syntax error would break the language grammar, while a runtime error would involve an executed operation failing; neither is stated in this successful but incorrect calculation.

Question 5 of 10

What final value does this Python code store in total?

total = 1
for n in [4, 7, 2]:
    total = total + n
  1. 13
  2. 3
  3. 15
  4. 14
Show answerHide answer
Answer: D
The total starts at 1, then becomes 5 after adding 4, 12 after adding 7, and 14 after adding 2. The loop keeps the result of each previous update. Getting 13 would ignore the initial value and calculate only the sum of the list, which is not the full computation written here.

Question 6 of 10

In Python, label = "18" is followed by label = 18. The name can change type, but "18" + 2 raises an error. How are these two facts described?
  1. Static typing with strict mixed-addition rules
  2. Dynamic typing with strict mixed-addition rules
  3. Dynamic typing with automatic numeric addition
  4. Static typing with automatic numeric addition
Show answerHide answer
Answer: B
Rebinding a name from text to an integer is consistent with dynamic typing, while the failed addition shows that this operation does not silently convert the text to a number. Checking time and conversion behavior are separate questions. Dynamic typing does not mean that incompatible types are automatically combined.

Question 7 of 10

What does first contain after this Python code runs?

first = [4, 8]
second = first
second.append(6)
  1. [4, 8]
  2. [6]
  3. [4, 8, 6]
  4. [4, 8, [6]]
Show answerHide answer
Answer: C
Both names refer to one list because assigning the reference does not copy its contents. Appending through second mutates that shared list, so first also shows the added 6. Keeping [4, 8] would require a separate list, such as one created by copying before the append.

Question 8 of 10

A report program is divided into read_data(), calculate_total(), and save_report(), each containing ordered commands. What organization does this illustrate?
  1. Procedural programming
  2. Logic programming
  3. Pure functional programming
  4. Class inheritance
Show answerHide answer
Answer: A
Procedural programming groups imperative steps into named tasks, which is exactly how these report operations are organized. The function names alone do not establish pure functional programming. In particular, reading and saving are effects, and nothing in the description requires calculations to avoid shared changes.

Question 9 of 10

A recursive Python function returns 0 at n == 0; otherwise it calls itself with n + 1. Starting at positive 2, why does the written base case not make the calculation finish?
  1. The inputs reach zero, but returning zero restarts the recursion.
  2. The base case guarantees termination even for increasing inputs.
  3. The recursion unwinds toward zero before checking the base case.
  4. The recursive inputs move away from zero instead of reaching it.
Show answerHide answer
Answer: D
The successive inputs are 2, 3, 4, and so on, so none meets the stopping test. A base case must actually be reached by the recursive progression; its presence in the source alone is insufficient. Returning 0 in that unvisited branch cannot change the arguments supplied by the calls that do execute.

Question 10 of 10

A dashboard request performs two steps in sequence: 180 ms waiting for network data, then 20 ms formatting it locally. A new language would reduce formatting to 5 ms without changing the network wait. What is the best assessment of this change alone?
  1. It saves 15 ms, while the network remains the main delay.
  2. It saves 150 ms, applying the fourfold speedup to the request.
  3. It saves 5 ms, because the new formatting time is the saving.
  4. It saves 20 ms, because compilation removes the formatting step.
Show answerHide answer
Answer: A
The stated sequential work drops from 200 ms to 185 ms, saving 15 ms. The 180 ms network wait is unchanged and still dominates the request. Applying the formatter's fourfold speedup to the entire request would incorrectly assume the same improvement affects the network wait too.
Set 2: Apply type, object, and query rules10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A team can compile the same portable source for two different ISAs. What is the correct relationship between its source and native outputs?
  1. The source is shared; both outputs contain the same ISA code.
  2. The source must differ; each output targets its own ISA.
  3. The source is shared; each output targets its own ISA.
  4. The source must differ; both outputs contain the same ISA code.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A runtime translates a frequently executed method into native instructions after the application has started. What technique is this?
  1. Just-in-time compilation
  2. Ahead-of-time compilation
  3. Static linking
  4. Lexical analysis
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

With a = 8 and b = 12 in Python, what values do a + b / 2 and (a + b) / 2 produce, respectively?
  1. 10.0 and 10.0
  2. 14.0 and 14.0
  3. 10.0 and 14.0
  4. 14.0 and 10.0
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

A C function releases an allocated block, but later code reads it through an old pointer. What fault does that later read create?
  1. A memory leak
  2. Use after free
  3. A double free
  4. A buffer overflow
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A class defines a balance attribute and a deposit method. Two accounts are created with different starting balances. Where do those distinct balances belong?
  1. In the shared deposit definition
  2. In the two account instances
  3. In the class name itself
  4. In the constructor's parameter names
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A bicycle object stores a brake-system object and asks it to stop the wheels. What relationship best matches this design?
  1. Composition: the bicycle has a brake system.
  2. Inheritance: the bicycle is a brake system.
  3. Overriding: the bicycle replaces the brake system.
  4. Duck typing: the bicycle is a brake-system subclass.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

From [1, 4, 7, 10], a pipeline keeps values greater than 3, subtracts 1 from each retained value, then sums them. What is the result?
  1. 21
  2. 17
  3. 18
  4. 22
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A database may execute an unchanged SQL query by scanning rows or using an available index. What chooses between those plans?
  1. The SQL query parser
  2. The database linker
  3. The SQL type checker
  4. The query optimizer
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A Python class method uses a loop to update a list and a pure function to transform each item. How should its styles be described?
  1. Object organization that excludes imperative work
  2. Functional organization that makes every method pure
  3. Object organization with imperative and functional work
  4. Imperative organization that prevents object methods
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

What are x and y after this Python code?

x = [12]
y = x
x = x + [3]
y.append(6)
  1. x is [12, 3]; y is [12, 6].
  2. x is [12, 3, 6]; y is [12, 3, 6].
  3. x is [12, 3, 6]; y is [12].
  4. x is [12, 6]; y is [12, 3].
Show answerHide answer

Save this lesson to see this answer.

Set 3: Trace calls, effects, and object contracts10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A compiler replaces minutes = 60 * 12 with the precomputed value 720. What optimization is used, and what must it preserve?
  1. Dynamic linking, preserving library file names
  2. Lexical analysis, preserving every source character
  3. JIT warmup, preserving runtime call counts
  4. Constant folding, preserving required behavior
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does this Python program print?

score = 11
def change():
    score = 4
    return score + 2
print(change())
print(score)
  1. 6, then 4
  2. 6, then 11
  3. 4, then 11
  4. 13, then 11
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

What is items after the call?

items = [3]
def revise(values):
    values.append(8)
    values = [1]
    values.append(5)
revise(items)
  1. [3, 8]
  2. [1, 5]
  3. [3, 8, 5]
  4. [3]
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

Two objects refer to each other. All references from live program roots to those objects are removed. Why can reference counting alone miss them, while tracing can reclaim them?
  1. Internal counts reach zero, so tracing is needed to release zero-count objects.
  2. Internal counts remain positive, so both objects must still be reachable.
  3. Internal counts remain positive, but neither object is reachable.
  4. Internal counts equal the number of roots, so both objects have zero counts.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

Starting from 120, one operation applies a 25% discount and another adds 8. What final values result from discount-then-add and add-then-discount?
  1. 96 and 98
  2. 98 and 98
  3. 98 and 96
  4. 96 and 96
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

An account method checks a withdrawal amount before changing the stored balance. What design idea keeps this rule next to the update it controls?
  1. Encapsulation
  2. Inheritance
  3. Constant folding
  4. Type inference
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A specialized account charges a fee of 2 for every successful withdrawal and must never have a negative balance. Its balance is 43. What should happen to a request to withdraw 42?
  1. Accept the request and leave the balance at 1.
  2. Accept the request and leave the balance at −1.
  3. Refuse the request after reducing the balance to 1.
  4. Refuse the request and keep the balance at 43.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A function returns a fixed calculation from its arguments and also prints a log message. Is it pure, and why?
  1. Yes; its result is predictable.
  2. No; printing is a side effect.
  3. Yes; logging is not stored data.
  4. No; all code has side effects.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A webpage registers showDetails for a click event. Why can that same handler still use object methods and imperative assignments?
  1. Events make it object-oriented only if it changes attributes.
  2. Events decide when it runs; its body can use other styles.
  3. Events require pure callbacks, so assignments must move.
  4. Events make it declarative, so the engine picks its updates.
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A new analysis feature must use an established Python-only library, and the team already maintains Python services within the required resource budget. What stated facts support choosing Python?
  1. Raw loop speed and the absence of runtime memory costs
  2. Browser compatibility and enforced Python type hints
  3. Exact floating-point math and every hardware target
  4. Required library support and existing team experience
Show answerHide answer

Save this lesson to see this answer.

Set 4: Reason across execution and design boundaries10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

A high-level statement becomes a different number of instructions in two optimized builds, but both meet the same language behavior. Does the different instruction count make either build incorrect?
  1. No; one statement need not map to a fixed instruction count.
  2. Yes; each statement must map to one machine instruction.
  3. Yes; high-level portability requires identical native output.
  4. No; optimized output is allowed to change required results.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

A native executable uses dynamic linking. The recipient has no compiler and is missing a required compatible shared library. Which stated absence explains why it may fail to start?
  1. The ahead-of-time compiler is missing.
  2. The original source file is missing.
  3. The required shared library is missing.
  4. The compiler's parsing tree is missing.
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A compatible JVM runs the same class file on two ISAs and JIT-compiles its busiest method on each. A teammate claims this proves the class file already contains native instructions for both processors. What is the correct diagnosis?
  1. The file holds native code; the JVM only links platform libraries.
  2. The file holds bytecode; the JIT output is also ISA-independent.
  3. The file holds source text; the JVM must recompile the original source.
  4. The file holds bytecode; each JIT generates its own native code.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

With n = 0 in Python, compare n != 0 and 36 / n > 4 with 36 / n > 4 and n != 0. What happens?
  1. Both are false because they contain the same two tests.
  2. The first is false; the second raises a division error.
  3. Both raise a division error because both mention division.
  4. The first raises an error; the second is false.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

What final value is stored in total?

def bump(x):
    return x + 2
total = 0
for n in [2, 5, 8]:
    if n > 3:
        total = total + bump(n)
  1. 17
  2. 15
  3. 19
  4. 21
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A compiler infers all local variable types before execution, although the programmer writes no type annotations. What checking approach does this demonstrate?
  1. Dynamic typing with runtime inference
  2. Weak typing with no type information
  3. Dynamic typing with explicit conversion
  4. Static typing with type inference
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A statically typed language infers an integer type from an initializer and permits a particular implicit numeric conversion. What can be concluded about those two features?
  1. Inference makes these variables dynamic despite the static checker.
  2. Implicit conversion means the inferred variable has no fixed type.
  3. Inference and conversion can coexist with static checking.
  4. Static checking means the conversion must be written explicitly.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

A report loops over objects and calls item.render(). A new unrelated class supplies a compatible render() method. No superclass is required by the Python caller. Why can the object join the loop without a type-specific branch?
  1. Common inheritance is required before its method can run.
  2. Duck typing accepts the object; dispatch selects its method.
  3. Matching method names make every receiver run one shared body.
  4. The caller must select the concrete class before the call.
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

A factory returns a function that multiplies its input by the factory's factor. The factor was 4, and the returned function is later called with 7. What result and mechanism explain this?
  1. 28, using a stack frame
  2. 11, using a closure
  3. 28, using a closure
  4. 28, using a type cast
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A team treats a popularity ranking as sufficient evidence that a language can run on its required deployment target. What compatibility check is missing?
  1. Whether its target platform supports the needed runtime
  2. Whether tutorials use the team's preferred indentation
  3. Whether its name ranks high in another popularity survey
  4. Whether it has a shorter release history than alternatives
Show answerHide answer

Save this lesson to see this answer.

Set 5: Diagnose limits and combine the ideas10 Practice Questions

Save this lesson to see every answer and practice every set.

Question 1 of 10

An interpreter-based implementation spends time executing bytecode. A second implementation of the same language adds a JIT and becomes faster on a long repeated calculation. What conclusion follows without changing the language's definition?
  1. The language became statically typed because it gained a compiler.
  2. Execution strategy changed while language semantics can remain the same.
  3. The first runtime executed source directly, so bytecode was not compiled.
  4. The shared language must change its operator meanings to gain speed.
Show answerHide answer

Save this lesson to see this answer.

Question 2 of 10

What does this Python program print?

value = 6
def step(value):
    value = value + 3
    return value
for n in [1, 2]:
    result = step(value)
print(value, result)
  1. 12 12
  2. 6 12
  3. 6 9
  4. 9 9
Show answerHide answer

Save this lesson to see this answer.

Question 3 of 10

A garbage-collected service stores each processed record in a global list that remains reachable. Removing local variables does not reduce retained record memory. What change addresses the stated cause?
  1. Remove references to obsolete records from the global list.
  2. Invoke collection repeatedly while leaving the archive unchanged.
  3. Delete each local binding while preserving the global archive entries.
  4. Replace the archive list with a copy containing the same records.
Show answerHide answer

Save this lesson to see this answer.

Question 4 of 10

Two procedures update the same global value. double() multiplies it by 2; add_three() adds 3. Starting from the same value, what is always the difference between double-then-add and add-then-double?
  1. Double-then-add gives a result 3 greater.
  2. Add-then-double gives a result 6 greater.
  3. Both orders always give the same result.
  4. Add-then-double gives a result 3 greater.
Show answerHide answer

Save this lesson to see this answer.

Question 5 of 10

A Java class marks its balance field private and exposes a validating withdrawal method. Code in an unrelated external class tries to assign a negative balance directly. What protects the class rule at that access point?
  1. The withdrawal method validates direct assignments.
  2. The assignment creates a separate local balance.
  3. Private access permits writes but blocks all reads.
  4. The compiler rejects access to the private field.
Show answerHide answer

Save this lesson to see this answer.

Question 6 of 10

A class inherits from a queue only to reuse storage, but it disables the queue's removal operation. Callers expecting a usable queue fail. What redesign addresses the stated relationship problem?
  1. Keep queue inheritance and hide removal from the subclass interface.
  2. Hold a queue internally and expose the operations the class promises.
  3. Keep queue inheritance and make removal silently do nothing.
  4. Keep queue inheritance and document that callers must avoid removal.
Show answerHide answer

Save this lesson to see this answer.

Question 7 of 10

A table readings has rows (level, active): (50, true), (65, true), (20, false), (85, true), and (90, false). A query requests SUM(level) where active = true AND level > 50. After a suitable index is added, what total is required, and may the execution plan change?
  1. 150; the plan may change while preserving the total.
  2. 200; the plan may change while preserving the total.
  3. 240; the plan may change while preserving the total.
  4. 150; the new index may change both the plan and the total.
Show answerHide answer

Save this lesson to see this answer.

Question 8 of 10

Prolog has parent(lea, noah). and parent(noah, sora). plus the rule grandparent(X, Z) :- parent(X, Y), parent(Y, Z).. What does grandparent(lea, Who) find?
  1. Who = noah
  2. Who = lea
  3. Who = sora
  4. Both Who = lea and Who = noah
Show answerHide answer

Save this lesson to see this answer.

Question 9 of 10

An imperative loop, an object method, and an SQL query must total amounts strictly above 50 in [50, 65, 20, 85]. What must their correct results be?
  1. 200 in all three versions
  2. 150 in all three versions
  3. 150 for the loop, 200 for the method and query
  4. 200 for the loop, 150 for the method and query
Show answerHide answer

Save this lesson to see this answer.

Question 10 of 10

A function returns a newly allocated list, and its caller stores that list for later use. Must the list disappear when the function's call frame is removed?
  1. Yes; ending the call overrides the caller's reference.
  2. Yes; returning copies its contents and frees it.
  3. No; its creating frame must remain active to keep it.
  4. No; the caller still holds a reference to the list.
Show answerHide answer

Save this lesson to see this answer.

Stuck on your own question? Homework Help

Key Terms

94 Key Terms, each with its definition

A

Abstraction
Abstraction hides implementation details behind a simpler model or interface so a programmer can focus on the task being expressed.
A variable is an abstraction that lets code name a value without spelling out its physical memory address.
Ahead-of-time compilation
Ahead-of-time compilation translates code before program execution, moving translation work into a separate build stage.
Ahead-of-time compilation can produce a native executable that runs without its compiler being installed.
Aliasing
Aliasing occurs when different names or references lead to the same object, making its mutations visible through all of them.
After b = a, aliasing explains why appending through b can also change what is seen through a.
Allocation
Allocation reserves memory for program data, with the reserved storage remaining unavailable for reuse until released.
Creating a growing collection can require further allocation as it stores additional items.
Assembler
An assembler is a translator that encodes assembly-language instructions as machine code for a specific instruction set.
An assembler turns add eax, ebx into the corresponding x86-64 instruction bytes.
Assembly language
Assembly language represents low-level instructions using readable mnemonics and names for registers or storage locations.
An analyst reads assembly language to follow operations in a program whose high-level source is unavailable.
Assignment
Assignment binds a name or storage target to a value, using the current state to evaluate the right-hand expression first.
The assignment count = count + 1 reads the old count before replacing it with the incremented value.
Attribute
An attribute is a named piece of data or member associated with an object or class and accessed through it.
An account's balance attribute records its current state separately from another account's balance.

B

Base case
A base case gives a recursive result directly, allowing a finite calculation to stop making further recursive calls.
The factorial base case returns 1 for zero so the waiting multiplication calls can finish.
Bottleneck
A bottleneck is the part of a system that limits overall performance, making improvements elsewhere less effective.
If network waiting is the bottleneck, faster local arithmetic may barely change the request's total time.
Buffer overflow
A buffer overflow writes beyond the bounds of a block of storage, potentially damaging neighboring data or program state.
Writing index 10 of a ten-element C array creates a buffer overflow instead of adding an eleventh element.
Bytecode
Bytecode is an intermediate instruction form designed for a software runtime instead of direct execution by the host processor.
CPython generates bytecode from a valid Python file before its virtual machine executes the program.

C

Call stack
The call stack tracks active function calls in nested order so execution can return to the correct caller.
A call to an area function adds an entry to the call stack and removes it when its result returns to the caller.
Callback
A callback is a function supplied to other code so that code can invoke it when an operation or event requires it.
Passing showMenu as a callback lets a button system call that function when the user clicks.
Class
A class defines the structure and behavior of its instances, describing their attributes and available methods.
A library's Loan class can define the member, book, due date, and renewal operation for each loan.
Closure
A closure is a function together with access to variables from its creation environment, which can outlive the enclosing call.
A multiplier closure retains its factor after the function that created the multiplier has returned.
Coercion
Coercion is an implicit conversion performed by language rules when an operation needs a value in another type.
JavaScript coercion converts numeric text for multiplication but can concatenate it when using addition.
Compiler
A compiler translates source code into a target representation, which can be native machine instructions or intermediate bytecode.
A compiler can reject an incompatible assignment before producing the program that a user will run.
Composition
Composition builds an object from other objects it holds as components or collaborators, expressing a has-a relationship.
Composition gives a car an engine object whose methods the car can call without becoming a kind of engine.
Constant folding
Constant folding evaluates expressions made from constants during compilation while preserving the required program behavior.
Constant folding lets a compiler replace 60 * 60 * 24 with 86400 before the program starts.

D

Dangling pointer
A dangling pointer refers to storage that has already been released and is no longer a valid target for access.
A dangling pointer may still hold an old address even after that address has been reused for other data.
Data type
A data type specifies a set of possible values and the operations allowed on them, guiding their representation and use.
A string data type supports text operations even when the characters in the value happen to be digits.
Declarative programming
Declarative programming specifies a required result or relationship while leaving execution details to a language engine.
Declarative programming lets an SQL query request qualifying rows without specifying a row-by-row loop.
Domain-specific language
A domain-specific language is designed for a particular problem area, such as database queries, patterns, or page styling.
A domain-specific language such as SQL can express a data request more directly than a hand-written search loop.
Double free
A double free attempts to release the same allocated memory block twice, breaking the allocator's expected lifecycle.
Calling free again on an already released allocation can create a double free and unpredictable behavior.
Duck typing
Duck typing accepts an object based on the operations it provides, without necessarily requiring a shared declared superclass.
Python duck typing lets an unrelated object join an area calculation if it supplies a compatible area() method.
Dynamic dispatch
Dynamic dispatch selects a method implementation during execution according to the object receiving the call.
Dynamic dispatch routes shape.area() to the rectangle implementation when the receiver is a rectangle.
Dynamic typing
Dynamic typing checks types of values during execution and allows a name to refer to values of different types over time.
Python dynamic typing permits a name to move from an integer to a string, but incompatible arithmetic still fails.

E

Ecosystem
An ecosystem is the surrounding collection of libraries, tools, documentation, and community support available for a technology.
A strong data-analysis ecosystem can make Python practical for a team even when raw loop speed is not its main advantage.
Encapsulation
Encapsulation groups data with controlled operations that maintain its rules and limit unsafe changes from other code.
Encapsulation lets an account refuse an excessive withdrawal before its balance can become negative.
Event handler
An event handler is a function or operation registered to respond when a particular event is delivered by the runtime.
A click event handler can update a menu while another handler processes an incoming network message.
Event-driven programming
Event-driven programming organizes work around handlers invoked when events occur, such as clicks or incoming messages.
Event-driven programming waits for a button click before running the registered interface update.
Expression
An expression is a piece of code evaluated to produce a value, which can then be used by a surrounding statement.
The expression width * height supplies the value returned by a rectangular-area function.

F

First-class function
A first-class function can be treated as a value, including assignment to a name, passing as an argument, and return from a call.
A first-class function can be supplied to map as the operation to apply to each collection item.
Floating-point number
A floating-point number represents a value with limited binary precision, making most decimal fractions approximate.
A floating-point number can introduce a tiny difference between 0.1 + 0.2 and the stored value of 0.3.
Functional programming
Functional programming organizes calculations by composing functions and producing new values while limiting shared mutation.
Functional programming can express a report calculation as selection, transformation, and combination of values.

G

Garbage collection
Garbage collection automatically identifies and reclaims storage for objects the running program can no longer reach.
Tracing garbage collection can reclaim an unreachable group even when its objects still refer to each other.

H

Heap
The heap is memory used for dynamically allocated objects, including objects that may outlive the function creating them.
A returned list can remain on the heap after the creating function has completed and its call frame is gone.
High-level language
A high-level language expresses programs through concepts such as variables and functions while hiding many hardware details.
A high-level language lets a developer request a sum without choosing which processor registers to use.
Higher-order function
A higher-order function takes a function as an argument or returns a function, allowing behavior itself to be combined.
The higher-order function filter takes a predicate that decides which input values to retain.

I

Immutability
Immutability means an existing value cannot be changed in place; an update must produce or select a different value.
Immutability keeps an original collection available when a transformation constructs a new version.
Imperative programming
Imperative programming expresses a computation as ordered commands that update program state through assignments and control flow.
Imperative programming makes an accumulator's new value explicit on every pass through a loop.
Implementation
A language implementation is the software that provides a way to translate and execute programs written in that language.
CPython and PyPy are different implementations, so Python code can use different execution strategies.
Information hiding
Information hiding keeps implementation details behind an interface so other code depends on promised operations instead.
Information hiding allows an account to change its storage representation while preserving deposit behavior.
Inheritance
Inheritance defines a specialized class from an existing class, reusing or extending behavior through an is-a relationship.
Inheritance suits a savings account that must still satisfy the behavior expected of a bank account.
Instruction set architecture
An instruction set architecture defines the operations a processor family supports and the encodings used to represent them.
An x86-64 executable targets a different instruction set architecture from native ARM machine code.
Interface
An interface specifies operations that implementing types must provide, letting callers rely on a common contract.
A shape interface can require an area() method while leaving each shape to calculate it differently.
Interpreter
An interpreter carries out a program through a running software system, which can execute source or an internal representation.
An interpreter can run a short script without first giving the user a separate native executable.

J

Just-in-time compilation
Just-in-time compilation generates native machine code during program execution, often for frequently used parts.
Just-in-time compilation can improve a long-running loop after the runtime observes repeated execution.

L

Lambda
A lambda is a function expression without a declared function name, useful for supplying a small operation as a value.
The Python lambda lambda n: n * n supplies a squaring operation to a transformation pipeline.
Lexical analysis
Lexical analysis splits source text into meaningful tokens, including identifiers, literals, operators, and punctuation.
During lexical analysis, price * 3 is separated into a name, a multiplication operator, and a number.
Linker
A linker connects translated object files and required library code, resolving references between the parts of a program.
A linker connects a source file's translated call to the printing routine supplied by a library.
Logic error
A logic error makes a program compute an unintended result even though its executed operations can complete successfully.
Using a + b / 2 to average two values is a logic error because the division happens before addition.
Logic programming
Logic programming states facts and rules, then searches for values that make a question satisfy those relationships.
Logic programming can derive a grandparent relationship by connecting two known parent relationships.

M

Machine code
Machine code is a sequence of binary-encoded processor instructions that follow a particular instruction set architecture.
The processor executes machine code after a compiler or assembler has produced compatible instructions.
Memory leak
A memory leak retains allocated storage that is no longer needed, reducing the memory available for useful work.
An ever-growing cache with no eviction can cause a memory leak even in a garbage-collected application.
Memory safety
Memory safety prevents classes of invalid memory access, such as accessing released storage or writing outside valid bounds.
Memory safety can block a dangerous access while the program still contains an unrelated mistake in its calculation.
Method
A method is an operation associated with a class or object, often acting on the state of the instance receiving the call.
A withdrawal method checks the requested amount before changing the receiving account's balance.
Multi-paradigm language
A multi-paradigm language supports multiple styles of organizing computation, allowing different approaches within one program.
Python is a multi-paradigm language in which a class method can use an imperative loop and a functional transformation.
Mutation
Mutation changes an existing object in place, so any reference to that same object observes the updated contents.
Appending an item to a shared list is mutation; assigning a name to a new list changes only that binding.

O

Object
An object is a program entity that combines data with available operations, often as a particular instance of a class.
Two account objects can use the same withdrawal method while keeping their own distinct balances.
Overflow
Overflow occurs when an arithmetic result lies outside the range supported by a fixed-width numeric representation.
Java integer overflow wraps beyond its maximum value, while C signed overflow has undefined behavior.
Overriding
Overriding gives a subclass its own implementation of an inherited method while keeping the operation available to callers.
Overriding a withdrawal method can add a fee while preserving the rule against a negative balance.
Ownership
Ownership assigns responsibility for a value's lifetime, allowing compiler rules to check access and arrange memory release.
Rust ownership rejects safe-code access through a reference that would outlive the value it points to.

P

Parameter
A parameter is a name in a function definition that receives a value supplied by an argument when the function is called.
The parameter width receives 3 when the caller evaluates area(3, 4).
Parsing
Parsing checks tokens against a language grammar and represents the structure of expressions and statements.
Parsing records that the multiplication in total = price * 3; belongs inside the assignment.
Platform
A platform is the hardware and software environment on which a program must run, including supported runtimes and system services.
A browser platform can run JavaScript directly through its engine and compiled modules through WebAssembly.
Polymorphism
Polymorphism lets a shared operation work with different object types while each object supplies its appropriate behavior.
Polymorphism lets a report call area() on circles and rectangles without a separate branch for each.
Procedural programming
Procedural programming groups imperative commands into named functions or procedures, each handling a coherent task.
Procedural programming separates reading data, calculating a result, and displaying a report into named tasks.
Programming paradigm
A programming paradigm is a style for organizing computation, with particular ways to express data, operations, and control.
One programming paradigm may emphasize state updates while another emphasizes composing pure functions.
Pure function
A pure function returns the same result for the same arguments and performs no side effects beyond returning that result.
A pure function for a fixed multiplier can be tested without arranging global state or capturing output.

Q

Query optimizer
A query optimizer chooses a plan for carrying out a database query while preserving the result the query requests.
A query optimizer may use an available index after the data changes, without a rewrite of the SQL request.

R

Recursion
Recursion solves a problem through calls to the same function, commonly reducing it toward a case that needs no further call.
Factorial recursion multiplies by successively smaller integers until reaching the defined result for zero.
Reference
A reference is a value that identifies an object and allows access to it without duplicating the object's stored data.
Copying a reference to a list gives another name access to the same list and its later mutations.
Reference counting
Reference counting tracks references to an object and normally reclaims its storage when the count reaches zero.
CPython reference counting needs help from cycle collection for unreachable objects that keep one another referenced.
Regular expression
A regular expression describes a text pattern for a matching engine, using rules for characters, repetition, and grouping.
The regular expression [0-9]{3}-[0-9]{4} matches a three-digit group followed by a hyphen and four digits.
REPL
A REPL is an interactive read-evaluate-print loop that accepts an entry, evaluates it, shows a result, and waits for another.
A learner uses a REPL to test a small expression and see its result before entering another expression.
Runtime error
A runtime error occurs when an executed operation cannot proceed under the language rules or available conditions.
Dividing by zero in Python produces a runtime error when execution reaches that calculation.

S

Scope
Scope is the part of a program where a particular name is available, independently of the lifetime of its referenced object.
A local name's scope does not extend to a caller just because the function returns the object it named.
Semantics
Semantics is the meaning assigned to program constructs, including how operators behave and how execution changes values.
The semantics of integer division explains why the same-looking division can give different results across languages.
Short-circuit evaluation
Short-circuit evaluation skips a logical operand when an earlier operand already determines the expression's outcome.
Short-circuit evaluation lets a nonzero check prevent an unsafe division on the right side of and.
Side effect
A side effect is an observable action beyond returning a result, including output or changes to shared program data.
Writing a report file is a side effect even if the function leaves every global numeric variable unchanged.
SQL
SQL is a language for working with relational data, with queries that state requested fields, row conditions, and ordering.
SQL can request the names of students above a grade threshold and sort those names alphabetically.
Stack frame
A stack frame stores information associated with one active function call, such as local bindings and the return location.
Each unresolved recursive call needs its own stack frame unless an implementation can optimize it away.
Statement
A statement is a complete program instruction, such as an assignment, a conditional block, or a loop.
The statement total = price * 3 assigns the result of a multiplication to a variable.
Static typing
Static typing checks type compatibility before execution, allowing many invalid combinations to be rejected during development.
Static typing lets a Java compiler reject text used to initialize an integer variable before the program runs.
Structured programming
Structured programming organizes control through sequences, selections, loops, and calls instead of arbitrary jumps.
Structured programming lets a reader follow nested blocks to see which statement will execute next.
Syntax
Syntax is the set of rules defining which arrangements of tokens make valid expressions, statements, and programs.
Python syntax requires a colon after an if condition and indentation for the controlled block.

T

Token
A token is a meaningful piece of source text, such as a keyword, identifier, literal, operator, or punctuation mark.
The semicolon in a C assignment is a token even though it names neither a variable nor a numeric value.
Trace table
A trace table records relevant variable values after executed steps so the path and changing state can be followed.
A trace table for an accumulator records the new total after each item passes through the loop.
Turing completeness
Turing completeness is the ability to express any computable algorithm when idealized unbounded time and memory are available.
Turing completeness does not tell a team whether a language has the browser support or libraries its project needs.
Type inference
Type inference derives types from the surrounding code, allowing static checks even when type annotations are omitted.
Type inference lets a compiler determine an integer type from an initializer instead of a written type label.

U

Use after free
Use after free accesses storage through a pointer after its allocation has been released, causing invalid memory access.
Use after free can overwrite a new object that happens to occupy a previously released block.

V

Virtual machine
A virtual machine is software that provides an execution model for instructions, such as the bytecode used by a language runtime.
A compatible Java virtual machine lets the same class file run on computers with different processors.

About this lesson

By StudeliReport a mistake

Make your own Study Materials

Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.

Get Started