Programming Fundamentals - Variables, Types, and Operators
Every program — from a one-line script to a planet-scale distributed system — is built from three fundamental ideas: variables that hold data, types that describe what that data looks like, and operators that combine and transform it.
Master these three, and every language becomes easier to learn.
Variables
A variable is a named reference to a value stored somewhere in memory. The name lets you read and update the value without caring about its physical address.
age = 30 # name "age" points to the integer 30
name = "Alice" # name "name" points to the string "Alice"
age = age + 1 # rebind "age" to a new integer (31)
How Variables Work in Memory
Conceptually, a variable is a label attached to a memory location:
Name Address Value
age → 0x7ffc... 31
name → 0x7ffd... "Alice"
Different languages implement this differently:
- C/C++: a variable IS the memory slot. Assignment overwrites the bytes.
- Java/Python/JS: a variable is a reference to an object. Assignment changes which object the name points to.
a = [1, 2, 3]
b = a # b points to the SAME list as a
b.append(4)
print(a) # [1, 2, 3, 4] — both names see the mutation
Declaration vs. Initialization vs. Assignment
| Step | Meaning | Example (TypeScript) |
|---|---|---|
| Declaration | Introduce the name | let count: number; |
| Initialization | Give it its first value | count = 0; |
| Assignment | Change its value later | count = 5; |
In many languages these happen in one line:
let count: number = 0;
Scope
Where a variable is visible in your program:
let global = 1; // visible everywhere in this module
function outer() {
let local = 2; // visible only inside outer()
if (true) {
let block = 3; // visible only inside this if-block
}
// console.log(block); // ReferenceError — out of scope
}
- Global scope: visible everywhere
- Function scope: visible inside a function
- Block scope: visible inside
{ ... }(added withlet/constin JS)
Mutability — let vs const
let counter = 0; // can be reassigned
counter = 1; // OK
const PI = 3.14159; // cannot be reassigned
// PI = 3.14; // TypeError
const user = { name: 'Alice' };
user.name = 'Bob'; // OK — the binding is constant, the object is not
// user = {}; // TypeError — can't reassign the binding
Rule of thumb: default to const. Use let only when you genuinely need to reassign.
Naming Conventions
| Convention | Used For | Example |
|---|---|---|
camelCase | Variables, functions (JS, Java) | userName, totalCount |
snake_case | Variables, functions (Python, Rust) | user_name, total_count |
PascalCase | Classes, types | UserProfile, OrderService |
SCREAMING_SNAKE_CASE | Constants | MAX_RETRIES, API_KEY |
Good names describe intent, not implementation:
# Bad
d = 86400
# Good
seconds_per_day = 86400
Types
A type describes the shape of a value: what it can hold and what you can do with it. 5 + 5 makes sense; "hello" + true may or may not, depending on the language's rules.
Primitive Types
The atomic, indivisible types every language provides:
| Type | Example | Typical Size |
|---|---|---|
| Integer | 42, -7 | 32 or 64 bits |
| Float / Double | 3.14, -0.001 | 32 or 64 bits (IEEE 754) |
| Boolean | true, false | 1 bit (often padded to 8) |
| Character | 'a', '9' | 1 byte (ASCII) or 4 (Unicode) |
| String | "hello" | variable (sequence of chars) |
| Null / None / nil | absence of a value | language-dependent |
Composite Types
Built from primitives:
# List / Array — ordered collection
nums = [1, 2, 3, 4]
# Tuple — fixed-size, often immutable
point = (10, 20)
# Dictionary / Map / Object — key-value pairs
user = {"name": "Alice", "age": 30}
# Set — unordered, unique elements
tags = {"python", "web", "api"}
Static vs. Dynamic Typing
| Static (C, Java, TypeScript, Rust, Go) | Dynamic (Python, JavaScript, Ruby) | |
|---|---|---|
| Type known at... | Compile time | Runtime |
| Type errors caught... | Before running | When the line executes |
| Speed | Faster — compiler optimizes | Slower — type checks at runtime |
| Flexibility | More verbose | More flexible, faster prototyping |
// TypeScript — static
let age: number = 30;
age = "thirty"; // ❌ compile error
# Python — dynamic
age = 30
age = "thirty" # ✅ runs fine — name now bound to a string
Strong vs. Weak Typing
Orthogonal to static/dynamic — about whether the language implicitly coerces types.
// JavaScript — weakly typed
"5" + 3 // "53" (number coerced to string)
"5" - 3 // 2 (string coerced to number)
[] + {} // "[object Object]"
# Python — strongly typed
"5" + 3 # TypeError: can only concatenate str to str
Type Inference
Modern static languages figure out types from context, so you write less:
let count = 0; // inferred as number
const name = "Alice"; // inferred as string
const items = [1, 2, 3]; // inferred as number[]
let x = 42; // inferred as i32
let pi = 3.14; // inferred as f64
Type Casting / Conversion
Two flavors:
Implicit (automatic):
result = 5 + 3.14 # int 5 implicitly converted to float → 8.14
Explicit (you ask for it):
age_str = "30"
age_int = int(age_str) # explicit conversion
price = float("19.99")
flag = bool(1) # True
const n: number = Number("42");
const s: string = String(42);
const flag: boolean = Boolean(0); // false
Operators
Operators transform or compare values. They are the verbs of programming.
Arithmetic Operators
| Op | Meaning | Example | Result |
|---|---|---|---|
+ | Add | 5 + 3 | 8 |
- | Subtract | 5 - 3 | 2 |
* | Multiply | 5 * 3 | 15 |
/ | Divide | 10 / 3 | 3.33... |
// or div | Integer divide | 10 // 3 | 3 |
% | Modulo (remainder) | 10 % 3 | 1 |
** or ^ | Exponent | 2 ** 10 | 1024 |
Watch out for integer division differences:
# Python 3
10 / 3 # 3.333... (float division)
10 // 3 # 3 (integer division)
// Java
10 / 3 // 3 (integer division — operand types decide)
10.0 / 3 // 3.333...
Comparison Operators
Return a boolean.
| Op | Meaning |
|---|---|
== | Equal |
!= | Not equal |
<, > | Less than, greater than |
<=, >= | Less or equal, greater or equal |
5 == 5 # True
5 == "5" # False (different types)
[1, 2] == [1, 2] # True (deep equality)
JavaScript pitfall — == vs ===:
5 == "5" // true (coerces types — usually a bug)
5 === "5" // false (strict equality — preferred)
null == undefined // true
null === undefined // false
Always use === in JavaScript unless you have a specific reason.
Logical Operators
Combine boolean expressions.
| Op | Meaning |
|---|---|
&& or and | Both true |
|| or or | Either true |
! or not | Negation |
is_admin = True
is_active = False
can_edit = is_admin and is_active # False
can_view = is_admin or is_active # True
is_blocked = not is_active # True
Short-circuit evaluation — these stop as soon as the result is known:
const name = user && user.name; // safely access .name
const port = process.env.PORT || 3000; // fallback default
Bitwise Operators
Operate on individual bits — used in flags, low-level optimization, network protocols.
| Op | Meaning | Example |
|---|---|---|
& | AND | 0b1100 & 0b1010 = 0b1000 |
| | OR | 0b1100 | 0b1010 = 0b1110 |
^ | XOR | 0b1100 ^ 0b1010 = 0b0110 |
~ | NOT | ~0b1100 = ...11110011 |
<< | Left shift | 1 << 3 = 8 |
>> | Right shift | 8 >> 2 = 2 |
// Common pattern: feature flags as bitmask
const READ = 0b001; // 1
const WRITE = 0b010; // 2
const EXECUTE = 0b100; // 4
let perms = READ | WRITE; // 0b011
const canRead = (perms & READ) !== 0; // true
Assignment Operators
Shortcuts that combine assignment with another operation.
| Op | Equivalent |
|---|---|
x += 5 | x = x + 5 |
x -= 5 | x = x - 5 |
x *= 5 | x = x * 5 |
x /= 5 | x = x / 5 |
x %= 5 | x = x % 5 |
x ||= y | x = x || y (JS) |
x ??= y | x = x ?? y (JS, nullish) |
Ternary / Conditional Operator
A compact if/else that returns a value:
const status = age >= 18 ? "adult" : "minor";
status = "adult" if age >= 18 else "minor"
Operator Precedence
When multiple operators appear, precedence decides which runs first:
2 + 3 * 4 # 14, not 20 — * before +
(2 + 3) * 4 # 20 — parens force order
True or False and False # True — `and` before `or`
When in doubt, add parentheses. Clarity beats cleverness.
Putting It All Together
A small, realistic example using everything above:
// Calculate a shopping cart total with discount
interface CartItem {
name: string;
price: number;
quantity: number;
}
function calculateTotal(items: CartItem[], discountPercent: number = 0): number {
// Sum each item's subtotal
let subtotal = 0;
for (const item of items) {
subtotal += item.price * item.quantity; // arithmetic + assignment
}
// Apply discount only if it's in valid range
const discount = discountPercent > 0 && discountPercent <= 100
? subtotal * (discountPercent / 100)
: 0;
const total = subtotal - discount;
return Math.round(total * 100) / 100; // 2 decimal places
}
const cart: CartItem[] = [
{ name: "Book", price: 15.5, quantity: 2 },
{ name: "Pen", price: 1.99, quantity: 5 },
];
console.log(calculateTotal(cart, 10)); // 36.86
This 20-line function uses variables (subtotal, discount, total), types (CartItem, number, string), arithmetic (+, *, -, /), comparison (>, <=), logical (&&), assignment (+=), and the ternary operator.
Once these three building blocks feel natural, every other language concept — control flow, functions, classes, modules — is just a way of organizing them.
Summary
- Variables are named references to memory. Pay attention to scope and mutability.
- Types describe what data is and what you can do with it. Know if your language is static/dynamic and strong/weak.
- Operators transform and compare values. Know their precedence, and use parentheses when in doubt.
- Most bugs in beginner code trace back to these basics: a wrong type, a leaked scope, or an unexpected operator coercion.
Next up: Control Flow — how to make these values move through your program.