Variables, Types and Type Systems
What variables and constants are, which data types most languages share, and how static, dynamic, strong and weak typing differ.
Variables: named storage
A program needs to remember things while it runs: a price, a name, how many items are in a basket. A variable is a name attached to a stored value, so the program can refer to the value by name, read it, and replace it with a new one. "Variable" means exactly that: the value may vary while the program runs.
price ← 40
quantity ← 3
total ← price * quantity
quantity ← quantity + 1
The arrow reads "becomes": after the last line,
quantity holds 4, while total still holds
120 — it was computed from the old value and is not recalculated
automatically. Languages differ in what the name is attached to. In
C, a variable is a fixed piece of memory of a known size.
In Python, a variable is a name that refers to an object stored
elsewhere; the Python page
Variables and Data Types
explains that model, and
Memory Management shows where
the values live.
Identifiers and naming rules
The name of a variable, constant, function or type is called an identifier. Each language has rules for which names are allowed, and the rules are remarkably similar:
| Rule | Python | C | JavaScript |
|---|---|---|---|
| Allowed characters | letters, digits, _ |
letters, digits, _ |
letters, digits, _, $ |
| May start with a digit | no | no | no |
Case-sensitive (total ≠ Total)
|
yes | yes | yes |
| Keywords allowed as names | no (a few soft keywords excepted) | no | no (a few contextual keywords excepted) |
| Usual style for variables | unit_price |
unit_price |
unitPrice |
| Usual style for constants | VAT_RATE |
VAT_RATE |
VAT_RATE |
Spaces and hyphens are never allowed inside a name, which is why
multi-word names are joined with underscores (snake_case)
or capitals (camelCase). Not every language is
case-sensitive: SQL keywords and, in most databases, unquoted table
and column names are not. Beyond the rules, choose names that say
what the value means: seconds_elapsed tells
the reader more than t or x2.
Constants
A constant is a named value that
does not change after it has been set. Tax rates, the number of days
in a week, a maximum file size and π are typical constants. Giving
such a value a name has three benefits: the code explains itself
(price * VAT_RATE instead of
price * 0.21), the value is defined in one place when
it does need updating, and the language can stop you from changing
it by accident. How strict that protection is differs per language.
C: const
In C, the keyword const makes a variable read-only. The
compiler refuses any later assignment to it.
#include <stdio.h>
int main(void) {
const double VAT_RATE = 0.21;
double net = 100.0;
printf("%.2f\n", net * (1 + VAT_RATE));
return 0;
}
121.00
Adding the line VAT_RATE = 0.19; makes the program fail
to compile, with
error: assignment of read-only variable 'VAT_RATE'. The
mistake is caught before the program ever runs.
JavaScript: const
JavaScript's const declares a name that cannot be
assigned again. The check happens while the program runs. Note what
is constant: the binding between the name and the value,
not the contents of the value. An array declared with
const can still be changed.
const VAT_RATE = 0.21;
const prices = [10, 20];
prices.push(30); // allowed: the array itself is changed
console.log(prices);
VAT_RATE = 0.19; // not allowed: VAT_RATE is reassigned
[ 10, 20, 30 ] TypeError: Assignment to constant variable.
Python: a naming convention
Python has no constant keyword. By convention, a name written in
UPPER_CASE at the top of a module is a constant, and
programmers do not assign to it. The interpreter does not enforce
this; a type checker such as mypy can, if you annotate the name with
typing.Final.
VAT_RATE = 0.21 # a constant by convention only
DAYS_PER_WEEK = 7
net = 100.0
print(round(net * (1 + VAT_RATE), 2))
print(DAYS_PER_WEEK * 52)
121.0 364
Common data types
A value's data type says what kind of value it is, which tells the computer how to store it and which operations make sense. You can multiply two numbers, but not two names; you can make a name uppercase, but not a number. The names differ per language, but the same basic, or primitive, types appear nearly everywhere:
| Type | Holds | Examples | C | Python | JavaScript |
|---|---|---|---|---|---|
| Integer | whole numbers | 42, -7 |
int, long |
int |
number (also bigint) |
| Floating point | numbers with a fraction | 3.14, -0.5 |
float, double |
float |
number |
| Character | one single character | 'A', '7' |
char |
none (a string of length 1) | none (a string of length 1) |
| String | a sequence of characters: text | "Hello", "42" |
array of char |
str |
string |
| Boolean | true or false | true, false |
bool (<stdbool.h>) |
bool |
boolean |
In most compiled languages, number types have a fixed size. A C
int is usually 32 bits, so it can only hold values up
to 2,147,483,647; a larger result overflows. Python's
int grows as needed and never overflows. Floating-point
numbers trade exactness for range: 0.1 + 0.2 is not
exactly 0.3 in any of these languages.
Number Systems and Data Representation
explains both effects.
Characters and strings
A character (char) is a single letter,
digit, space or symbol. Underneath, it is stored as a number: its
code in a character set such as ASCII. C makes this visible, because
a char is simply a small integer:
#include <stdio.h>
int main(void) {
char grade = 'B';
printf("%c %d\n", grade, grade); /* as a character, then as a number */
printf("%c\n", grade + 1); /* the next character code */
return 0;
}
B 66 C
A string is a sequence of characters. Many
languages write a single character in single quotes and a string in
double quotes. In C, a character constant such as
'A' actually has type int (so
sizeof('A') is usually 4), and is normally stored in a
char; only in C++ is it a char itself.
"A" is a string of one character followed by a hidden
end marker. Python and JavaScript have no separate character type,
and accept either kind of quote for strings. How characters become
numbers is the subject of
Character Encoding.
Composite types
Composite (or compound) types are built from other
types. An array or list holds several values in
order, such as a week of temperatures. A record — a
struct in C, an object in JavaScript, a class instance
or dictionary in Python — groups named fields that belong together,
such as a customer's name, email address and date of birth. Strings
are often counted here too, since they are sequences of characters.
Data Structures covers the common
composite types and what each is good at.
Static and dynamic typing
Every value has a type, but languages differ in when they check that types are used correctly. With static typing, each variable has a type that is fixed in the source code, and the compiler checks every use of it before the program runs. C, C++, Java, C#, Go and Rust are statically typed. With dynamic typing, types belong to the values, not to the variables; a variable can refer to a number now and to a string later, and types are checked while the program runs. Python, JavaScript, Ruby and PHP are dynamically typed.
Static typing in C
In C, the type is part of the declaration, and it stays fixed for the variable's whole life. Assigning a value of another numeric type does not change the variable's type; the value is converted to fit.
#include <stdio.h>
int main(void) {
int quantity = 3; /* quantity is an int, for good */
double price = 2.50;
quantity = 7; /* fine: another int */
quantity = 7.9; /* converted to int: the fraction is dropped */
printf("%d items at %.2f\n", quantity, price);
return 0;
}
7 items at 2.50
Try to store text in quantity and the compiler
complains before anything runs. GCC 14 and later stop with an error;
older versions give the same message as a warning:
quantity = "three";
error: assignment to 'int' from 'char *' makes integer from pointer without a cast [-Wint-conversion]
Dynamic typing in Python
In Python, the same name can refer to values of different types over time. Each value knows its own type, and Python checks whether an operation fits only when that line runs.
quantity = 3
print(type(quantity).__name__)
quantity = "three"
print(type(quantity).__name__)
print(quantity.upper())
int str THREE
The flexibility has a price: a type mistake is only found when the faulty line runs — or not at all, if the operation happens to be valid for the wrong type:
def order_total(price, quantity):
return price * quantity
print(order_total(2.5, 4))
print(order_total("2.5", 4)) # a string by mistake: repeated, not multiplied
10.0 2.52.52.52.5
Static typing catches this class of mistake early and helps editors with autocompletion; dynamic typing makes short programs quicker to write and experiment with. The two approaches have moved toward each other: Python has optional type hints checked by tools such as mypy, and TypeScript adds static types on top of JavaScript.
Type inference
Static typing does not always mean writing every type out.
Type inference lets the
compiler work out a variable's type from the value it is given. In
Go, count := 3 declares an int; Rust's
let count = 3;, Java's var count = 3;,
C#'s var and C++'s auto do the same. The
type is still fixed at compile time — assigning a string to
count later is still an error — you just do not have to
spell it out.
Strong and weak typing
A second, separate question is what happens when an operation receives values of types that do not match. With strong typing, the language refuses to combine unrelated types and reports an error; you must convert explicitly. With weak typing, the language silently converts one value to another type so the operation can go ahead. Unlike static and dynamic, "strong" and "weak" are not formally defined: they describe a scale, and languages sit at different points on it.
JavaScript is the classic weakly typed example. Its
+ operator joins strings when either side is a string,
but * and - only work on numbers, so they
convert strings to numbers instead:
console.log("5" + 1); // number converted to string, then joined
console.log("5" * 2); // string converted to number, then multiplied
console.log("5" - 1);
console.log(true + 1); // true becomes 1
51 10 4 2
Python, which is also dynamically typed, is strongly typed. It does not guess what you mean:
print(5 * 2)
print("5" + 1)
10 TypeError: can only concatenate str (not "int") to str
The two scales combine freely. Python is dynamic and strong; JavaScript is dynamic and weak; Java is static and fairly strong. C is static but is often called weak, because it converts between numeric types silently and lets pointer casts reinterpret memory as any type.
Casting and coercion
Converting a value from one type to another happens in two ways. An
explicit conversion, also called a
cast, is written by the programmer:
int("42") in Python, (double) total in C,
Number("42") in JavaScript. An
implicit conversion, or coercion,
is done by the language on its own, following fixed rules. The
JavaScript examples above are coercions.
Even strongly typed languages coerce in safe cases, typically widening an integer to a floating-point number when the two are mixed. The C example below shows why the moment of conversion matters: integer division happens first unless you cast one operand before dividing.
#include <stdio.h>
int main(void) {
int total = 7;
int count = 2;
printf("%d\n", total / count); /* int / int: whole-number division */
printf("%.1f\n", (double) total / count); /* cast first, then divide */
double average = total / count; /* divided as ints, THEN converted */
printf("%.1f\n", average);
return 0;
}
3 3.5 3.0
age_text = "42"
print(int(age_text) + 1) # explicit: string to int
print(str(42) + " years") # explicit: int to string
print(3 + 0.5) # implicit: int widened to float
print(int(3.99)) # explicit: the fraction is cut off
43 42 years 3.5 3
A conversion can lose information (3.99 becomes 3) or fail (int("forty-two")
raises a ValueError), which is one reason strongly
typed languages make you ask for it. The Python details are in
Variables and Data Types.
No value: null, None and undefined
Sometimes a variable has no meaningful value yet, or a search finds nothing. Most languages have a special marker for "no value", and it is deliberately different from 0, from an empty string and from false, because those are all real values.
| Language | Marker | Meaning |
|---|---|---|
| C | NULL |
a pointer that points to nothing |
| Java, C# | null |
a reference that refers to no object |
| JavaScript | undefined |
not assigned at all (the automatic default) |
| JavaScript | null |
deliberately empty, set by the programmer |
| Python | None |
the single "no value" object |
| SQL | NULL |
unknown or missing value in a column |
let middleName; // declared, never assigned
console.log(middleName);
console.log(typeof null); // a famous historical quirk
console.log(null == undefined); // loose equality: treated as equal
console.log(null === undefined); // strict equality: different values
undefined object true false
prices = {"apple": 0.50, "pear": 0.75}
price = prices.get("mango") # no such key: get() returns None
print(price)
print(price is None)
None True