---
title: 'Basic Data Types'
source: 'https://academia.sh/en/courses/programming-fundamentals/basic-data-types'
course: 'Programming Fundamentals'
language: en
updated: '2026-08-17T18:08:26+00:00'
license: 'CC BY-SA 4.0'
---

# Basic Data Types

The concept of type, integers, real numbers, booleans, strings, and null values; static and dynamic typing.

The previous lesson bound a name to a number, then to a list, and the two behaved
differently. The list had an `append` operation; the number did not. The name for
this difference is **type**.

This lesson defines the concept of type and introduces the basic types that have a
counterpart in every programming language.

## What Is a Type

A **type** specifies two things together:

1. Which values are valid — the set of values.
2. Which operations are defined on those values.

The integer type's set of values is the integers within a certain range; its
operations are things like addition, subtraction, and comparison. The string type's
values are sequences of characters; concatenation and search are defined, subtraction
is not.

The previous course's conclusion applies directly here: memory contains only bit
patterns, and meaning comes from the interpretation rule applied to a pattern.
**A type is exactly that interpretation rule.** The programming-language answer to
whether the pattern `0x41424344` is an integer, a real number, or text is that
value's type.

## Integers

The integer type carries numbers with no fractional part. Two facts established in
the previous course apply here: fixed-width integers have a range, and overflow
occurs once that range is exceeded.

Languages diverge on this point. Some use fixed width and handle overflow either by
wrapping or by raising an error. Others — Python is one of them — grow integers as
large as needed; the only limit is memory.

Two division operations must be distinguished:

```python
print(7 / 2)        # 3.5   — real division
print(7 // 2)       # 3     — integer division (rounds down)
print(7 % 2)        # 1     — remainder
print(-7 // 2)      # -4    — rounding down also applies on the negative side
```

The rounding direction of integer division differs between languages: some round
toward zero, some round down. In code that works with negative numbers, this detail
changes the result.

## Real Numbers

The real number type carries fractional values in floating-point representation. The
previous course's warning applies here as well: values are approximate and are not
compared with equality.

```python
print(0.1 + 0.2 == 0.3)                    # False
print(abs((0.1 + 0.2) - 0.3) < 1e-9)       # True  — comparison with tolerance
```

Practical rule: quantities that require precision, such as money, are not held in
floating-point types; decimal-based types, or integers carrying the smallest unit,
are used instead.

## Booleans

The boolean type has only two values: true and false. These values are usually not
written directly; they are produced by comparisons.

```python
measurement = 12
print(measurement > 10)          # True
print(measurement == 10)         # False
```

Boolean values are the input to the conditional branching in the next topic. A
condition "being true" means it produces a value of this type.

Many languages also allow non-boolean values to be used in a condition: zero, an
empty string, and an empty collection count as false; everything else counts as
true. This convenience can reduce readability; code is less prone to
misunderstanding when what a condition tests is written explicitly.

## Strings

The string type carries sequences of characters. The previous course's lesson on
character encodings gave the detail underlying this type: a string is a sequence of
code points, and it is turned into bytes in memory according to an encoding.

```python
name = "value"
print(len(name))             # 5    — code point count
print(name[0])               # v    — indexing starts at zero
print(name.upper())          # VALUE
print("value" + " " + "1")   # value 1
```

Indexing starting from zero is a convention common to most languages, and it comes
from address arithmetic: an index states how many elements have been advanced past
the start.

In many languages strings are **immutable**: a string's content cannot be changed,
only a new string can be produced. This is why string concatenation is expensive
inside a loop; every concatenation creates a new object.

## Collections

Types that hold a sequence of values instead of a single value form the input to the
course's shared problem:

```python
measurements = [12, 18, 7, 25, 14]     # list: ordered, mutable
print(len(measurements))               # 5
print(measurements[0], measurements[-1])   # 12 14
print(sum(measurements) / len(measurements))   # 15.2
print(max(measurements))               # 25
```

This lesson introduces collections only enough to use them; their internal
structure, costs, and selection criteria are the subject of the **Data Structures**
course.

## Immutable and Mutable Types

Types are also distinguished by whether their values can be changed after creation.

An **immutable** value's content cannot be changed after it is created. Numbers, and
strings in most languages, belong to this class. Every operation that looks like a
change in fact produces a new value.

A **mutable** value's content can be updated in place. Lists belong to this class.

```python
text = "value"
new = text.upper()
print(text, new)          # value VALUE  — the original string did not change

measurements = [12, 18]
measurements.append(7)
print(measurements)             # [12, 18, 7]  — the list changed in place
```

The distinction gains weight when combined with the sharing rule from the previous
lesson. If two names are bound to the same object and the object is of a mutable
type, a change made through one is also visible through the other. If the object is
immutable, no such surprise occurs: since a change produces a new object, the other
name stays bound to the old value.

For this reason, shared data requires care when working with mutable types;
immutable types, in contrast, are safe to share. The same observation will be
repeated in the lesson on passing arguments to functions.

## Null Value

Most languages have a special value that denotes the state of "no value." This value
expresses a result that has not yet been computed or could not be found.

The cost of this convenience is that this value can turn up in unexpected places:
code that tries to operate on a null value raises a runtime error. Some languages
address this risk at the type level and mark values that may be null with a separate
type, so that whether the check was performed can be tested at compile time.

## When Type Is Checked

Languages test type compatibility at two different times.

**Static typing:** Types are known and checked during compilation. An incompatible
operation is caught before the program ever runs. In exchange, types must be
declared or inferred.

**Dynamic typing:** Type information is carried on values, and checking happens at
run time. The syntax is shorter; in exchange, the same error stays invisible until
the relevant line executes.

```python
number = 5
text = "5"
print(number + number)         # 10
print(text + text)             # 55   — string concatenation
# print(number + text)         # runtime error: incompatible types
```

The error in the third line surfaces under dynamic typing only once that line runs.
Under static typing, the same expression would have been rejected before running.
The principle established in the previous course's lesson on compilation stages
applies here as well: the question is **when** the error is seen.

## Summary

- A type specifies the set of valid values together with the operations defined on
  them; it is the programming-language name for the interpretation rule applied to a
  bit pattern.
- Integer division comes in two forms, and the rounding direction varies between
  languages.
- Real numbers are approximate; they are compared with tolerance rather than
  equality.
- Boolean values are produced by comparisons; a null value denotes absence and is a
  source of runtime errors when left unchecked.
- Strings are sequences of code points; in immutable types every change produces a
  new value, in mutable types the content is updated in place.
- Static typing catches errors before running, dynamic typing catches them while
  running.

## Next Step

Types have been defined; it is now time to build expressions from these values. The
next lesson takes up arithmetic, comparison, and logical operators, along with the
operator precedence rules that — in an earlier lesson — were the source of a logic
error.
