Labs, Capstone, Review, and Assessment
Task: calculate the average of passing scores.
Lab 1 — One problem, four paradigms
Task: calculate the average of passing scores.
Output (CPython 3.13.1): Output (SQLite via Python 3.13.1, scores 55, 40, 90, 70):
Create a ScoreReport or ExamResults type that owns or receives the score collection and exposes a method to compute passing-score statistics. Avoid introducing a class if it adds no meaningful invariant or behavior.
Compare: empty input, invalid scores, rounding, data size, clarity, and where data lives.
Lab 1, run for real The four styles above — plus extra tasks — are in the Paradigm Explorer: each tab shows the code and the output of a real run. The imperative and functional Python versions above both printed 71.66666666666667 on CPython 3.13.1; the SQL query on SQLite with the scores 55, 40, 90 and 70 returned the same value.
scores = [55, 40, 90, 70]
total = 0
count = 0
for score in scores:
if score >= 50:
total += score
count += 1
average = total / count if count else None
print(average) # 71.6666666666666771.66666666666667scores = [55, 40, 90, 70]
passing = list(filter(lambda score: score >= 50, scores))
average = sum(passing) / len(passing) if passing else None
print(average)SELECT AVG(score) AS average_passing_score
FROM exam_results
WHERE score >= 50;average_passing_score
71.66666666666667paradigmsLab 2 — State machine versus nested conditionals
Implement a course enrollment workflow with states:
Define valid transitions and reject invalid transitions. Implement one version with nested conditionals and another with an explicit transition table or state machine. Test duplicate events and concurrent requests.
Enrollment state machine Text equivalent: this is one possible transition table for the lab. Every arrow is an allowed transition (a CANCELLED enrollment could also be allowed to return to PENDING on a new request — that is a design decision to record). Any event not shown for the current state (for example, approve while NOT_ENROLLED) must be rejected. Defining the table first makes duplicate and concurrent events easy to test.
flowchart LR
S((start)) --> N[NOT_ENROLLED]
N -->|request| P[PENDING]
P -->|approve| E[ENROLLED]
P -->|cancel| C[CANCELLED]
E -->|cancel| CLab 3 — Functional core and imperative shell
Build a function that calculates course completion from lesson statuses. Keep the calculation pure; read lesson statuses from a repository and write the result in an outer service. Test the core without a database.
Lab 4 — Event-driven versus polling
Implement a status panel that updates when a course job completes. Compare event-driven notifications with periodic polling. Evaluate latency, server load, reconnect behavior, duplicate notifications, and stale state.
Lab 5 — Concurrent workload
Process a set of independent records using sequential execution and bounded concurrency. Measure performance with representative data and document errors, timeouts, cancellation, and output ordering.
Lab 6 — Build a small DSL
Define a simple rule language for course eligibility. Parse a limited grammar into an AST, validate fields and operators, and evaluate rules against typed data. Do not use unrestricted eval or arbitrary code execution.
Capstone — Design a Multi-Paradigm Learning Platform
Create a design or working prototype with:
User and course domain model Course search and filtering Enrollment and progress state transitions Declarative database queries
Pure functions for progress calculations OOP or module boundaries for domain services Event-driven UI updates
Asynchronous API calls with cancellation and error handling Optional stream processing for activity analytics
Tests for normal, boundary, and failure cases Problem statement and requirements Paradigm decision record Data model and state-transition diagram Code examples in at least three styles Unit and integration tests
Performance measurements for one relevant workload Explanation of trade-offs and rejected alternatives Refactoring plan for future growth
Final reflection on where each paradigm helped or hindered Suggested grading rubric Paradigm concepts and accuracy Design and rationale Correctness and edge cases Testing and maintainability Concurrency/asynchrony where relevant Performance evidence and documentation Reflection and trade-offs
Final assessment
What is a programming paradigm, and how is it different from a language?
Explain the difference between imperative and declarative programming.
What are state, mutation, aliasing, and side effects?
How do procedural programming and structured programming relate?
Explain encapsulation, abstraction, inheritance, polymorphism, and composition.
What makes a function pure? Why is purity useful?
What are higher-order functions and closures?
Compare Option/Maybe and Result/Either.
What is the difference between logic programming and constraint programming?
Compare static typing, dynamic typing, and type inference.
What is the difference between concurrency and parallelism?
Explain a race condition, deadlock, and backpressure.
How does the actor model manage state and communication?
What are the risks of unbounded asynchronous work?
Explain event-driven programming versus event sourcing.
How do reactive and dataflow models represent changing values?
What is a DSL, and what safety issues arise when implementing one?
Compare AoS and SoA data layouts and explain why profiling matters.
What are operational, denotational, and axiomatic semantics?
How should a team select a paradigm for a new feature?
Why is hybrid programming common in real applications?
Refactor a procedural solution into a functional core with an imperative shell and explain the benefits.
Give an example where OOP is clearer than a functional pipeline, and one where the reverse is true.
Explain why declarative code still requires knowledge of execution behavior.
Design tests for a concurrent state transition that must occur exactly once.
Quiz
? Lab 1 computes the average of passing scores. What must every version handle explicitly?
+ An empty list of passing scores (avoid dividing by zero). :: Every version returns None/NULL in that case.
- Negative zero. :: Not relevant to this task.
- Unicode normalisation. :: Scores are numbers.
- Thread safety. :: The task is single-threaded.
? Why define an explicit transition table in Lab 2? (select all that apply)
+ Invalid transitions can be rejected in one place. :: The table is the single source of truth.
+ Duplicate and concurrent events become testable cases. :: Each event-state pair has a defined outcome.
- It removes the need for tests. :: Tests are still required.
+ It is easier to review than deeply nested conditionals. :: The whole workflow is visible at once.
? In the capstone, why write a paradigm decision record?
+ To document the chosen paradigms, rejected alternatives and when to revisit the decision. :: It makes trade-offs explicit.
- Because a single paradigm is required. :: The capstone is deliberately multi-paradigm.
- To replace the architecture diagram. :: Both are deliverables.
- To avoid writing tests. :: Tests are a separate deliverable.