Skip to content
academia.sh

Lesson 03 / 18

Basic Data Types

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

Contents

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:

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.

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.

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.

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:

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.

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.

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.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close