Programming Languages lessons
What is a programming language?
Illustration: human writes code, translated into binary a CPU executes.
A programming language is a formal set of instructions that tells a computer what to do. Humans write code in a language they can read, and the computer translates it into operations it can execute.
Different languages exist because different problems need different tools. A language built for web pages looks nothing like one built for operating systems.
The evolution of programming languages
Programming languages have evolved over seven decades. Each generation solved problems the previous one could not.
Timeline of programming languages from the 1950s to the 2010s.
Early languages were close to hardware. Modern languages prioritise developer productivity and safety.
Machine code and assembly
Machine code is the raw binary that a CPU executes directly. Every instruction is a sequence of ones and zeros specific to a processor architecture.
Assembly language replaces binary with short mnemonics like MOV, ADD, and JMP. It maps one-to-one to machine code but is readable by humans.
; x86 Assembly — add two numbers
MOV EAX, 5 ; load 5 into register EAX
ADD EAX, 3 ; add 3 to EAX
; EAX now holds 8Practice: Match the abstraction level
Match each snippet to its abstraction level (machine code, assembly, high-level).
Compiled languages
A compiler translates an entire source file into machine code before the program runs. The output is a standalone binary that the OS executes directly.
Languages like C, C++, Go, and Rust are compiled. Compilation catches many errors before runtime, and the resulting binaries are fast.
The trade-off is a separate build step. You must recompile every time you change the source code.
Interpreted languages
An interpreter reads source code line by line and executes it immediately. There is no separate build step.
Python, Ruby, and JavaScript (in browsers) are interpreted. This makes development faster because you can run code instantly after editing.
Interpreted programs are generally slower than compiled ones because translation happens at runtime.
Compiled vs interpreted pipeline
Interactive compiled-vs-interpreted pipeline explorer.
Some languages use both. Java compiles to bytecode, which the JVM then interprets or JIT-compiles at runtime.
Practice: Order the compilation steps
Order the steps of compiling and running a program.
Static typing
In a statically typed language, every variable has a fixed type declared at compile time. The compiler rejects code that mixes types incorrectly.
// Java — static typing
int count = 10;
String name = "Alice";
count = name; // ERROR: incompatible typesJava, C, C++, Go, and Rust are statically typed. Errors are caught early, before the program ever runs.
Dynamic typing
In a dynamically typed language, variables can hold any type and can change type at any time. Types are checked at runtime, not compile time.
# Python — dynamic typing
value = 10
value = "hello" # No error, type changed
result = value + 5 # Runtime error: can't add str and intPython, JavaScript, and Ruby are dynamically typed. This gives flexibility but pushes type errors to runtime.
When type errors appear
Step through when static vs dynamic typing reports an error.
Static typing adds more code upfront but prevents entire categories of bugs. Dynamic typing is faster to write but requires more testing.
Practice: Categorise the languages
Sort languages into compiled/interpreted, then static/dynamic bins.
Language feature comparison
Comparison table of six languages across four properties.
Hello World in four languages
The simplest way to compare languages is to see how each one prints text to the screen.
# Python
print("Hello, World!")// JavaScript
console.log("Hello, World!");// Java
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}// C
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}Practice: Identify the language
Identify the language from each code snippet.
Object-oriented programming
Object-oriented programming (OOP) organises code around objects, which bundle data and the functions that operate on that data. A class is the blueprint, and an object is an instance of that class.
Key concepts are encapsulation (hiding internal state), inheritance (building on existing classes), and polymorphism (same interface, different behaviour).
# Python OOP
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "Woof!"
dog = Dog("Rex")
print(dog.speak()) # Woof!Functional programming
Functional programming treats computation as evaluating pure functions. A pure function always returns the same output for the same input and has no side effects.
Immutability is central: data is never modified in place. Instead, functions return new values. This makes code easier to test and reason about.
// JavaScript — functional style
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
// doubled is [2, 4, 6, 8, 10]
// numbers is unchangedOOP vs functional at a glance
Diagram of OOP vs functional programming, converging on multi-paradigm.
Most languages today are multi-paradigm. Python, JavaScript, and Rust all support both OOP and functional patterns.
Practice: Match the paradigm
Match each concept to its paradigm (OOP or functional).
Memory management
Every running program uses memory. The stack stores small, short-lived data like function calls and local variables. The heap stores larger data that lives beyond a single function.
Manual memory management (used in C and C++) requires the programmer to allocate and free memory explicitly. Forgetting to free causes memory leaks; freeing too early causes crashes.
Garbage collection (used in Java, Python, Go) automatically reclaims unused memory. It simplifies code but adds runtime overhead.
Stack vs heap
Diagram of stack call frames pointing into the heap.
Rust takes a unique approach with its ownership system. Memory is freed automatically when the owner goes out of scope, with no garbage collector needed.
Practice: Complete the feature table
Complete the memory-management column for each language.
Language popularity
Animated bar chart of approximate language popularity (2025).
Popularity shifts over time. Python rose sharply with the growth of data science and AI. Rust is the fastest-growing systems language.
Choosing the right language
There is no single best language. The right choice depends on what you are building.
For web front-ends, JavaScript or TypeScript are standard. For machine learning, Python dominates. For high-performance systems, C, C++, or Rust are the go-to choices.
Other factors matter too: team expertise, library ecosystem, community size, and hiring market all influence the decision.
Practice: Pick the right tool
Pick the best language for each real-world scenario.
The future of programming languages
Rust eliminates memory bugs at compile time through ownership rules. It is being adopted by major projects including the Linux kernel and Firefox.
Go was built at Google for cloud services. Its fast compilation, built-in concurrency, and simple syntax make it ideal for microservices and DevOps tools.
WebAssembly (Wasm) lets languages like C++, Rust, and Go run in the browser at near-native speed. It is expanding what web applications can do.
Practice: Name that language
Name each modern language (Rust, Go, Kotlin, Swift) from its syntax.
Summary
Programming languages sit on a spectrum from low-level (assembly) to high-level (Python). They can be compiled or interpreted, statically or dynamically typed.
OOP and functional programming are the two main paradigms, though most modern languages support both. Memory management varies from manual control to garbage collection to ownership systems.
The best language is the one that fits your problem, your team, and your constraints. Understanding how languages differ gives you the ability to learn any new one quickly.