Previous | Tutorial index | Next

Tutorial 7: Computer Programming Languages

Learning Objective

To be able to discuss computer programming languages.

7.1 Introduction: The Bridge Between Human Thought and Machine Logic

In Tutorial 3, we discussed the levels of abstraction in computing. At the bottom is digital logic, and at the top is human-readable high-level code. But that abstraction is only useful because we have programming languages—formal systems of syntax and semantics designed to translate human intent into machine-executable instructions.

There is no single "best" programming language. Each language is a tool designed for a specific purpose, era, and philosophy. A language that excels at system programming (like C) might be cumbersome for data science (where Python shines). This tutorial maps the landscape of programming languages, from the raw binary of machine code to the declarative queries of SQL, explaining why we have so many and how they are translated into the 0s and 1s the CPU understands.

7.2 Machine Language (1st Generation)

Machine language is the native tongue of the computer. It consists entirely of binary digits (0s and 1s) that the CPU can interpret and execute directly. No translation is needed—it is the only language the hardware truly understands.

Structure of a Machine Instruction: A typical machine instruction has two parts:

  1. Opcode (Operation Code): Specifies the operation to perform (e.g., ADD, SUB, MOV, JUMP).
  2. Operands: Specify the data or the location of the data (registers, memory addresses).

Example (x86 Architecture - simplified): The instruction to move the value 5 into the CPU's EAX register might look like: 10111000 00000101 00000000 00000000 00000000

Why we don't use it:

Historical Context: The very first programmers (like the "ENIAC women") literally plugged wires into patch panels to represent machine code. Later, programs were entered via punch cards, where a hole represented a 1 and no hole represented a 0.

7.3 Assembly Language (2nd Generation)

Assembly language was developed in the early 1950s as a human-readable mnemonic representation of machine code. Instead of writing 10111000, you write MOV EAX, 5.

Key Features:

The Assembler: An assembler is a program that translates assembly code into machine code. The translation is relatively straightforward because it's nearly a direct substitution.

Why assembly still matters:

Example (x86 Assembly):

section .data msg db 'Hello', 0 ; Define a string "Hello" section .text mov eax, 4 ; System call for 'write' (Linux) mov ebx, 1 ; File descriptor 1 (stdout) mov ecx, msg ; Pointer to the string mov edx, 5 ; Length of the string int 0x80 ; Invoke the kernel (system call)

7.4 High-Level Languages (3rd Generation)

High-level languages were created to free programmers from the tedious details of the hardware. They use syntax that is closer to human language or mathematics, making programming faster, safer, and more productive.

Key Characteristics:

7.4.1 The Pioneers (1950s)

7.4.2 The Systems Giants (1970s–1980s)

7.4.3 The Cross-Platform Era (1990s)

7.4.4 The Modern Era (Late 1990s–Present)

7.5 The Five Generations of Programming Languages

While the hardware generations (vacuum tubes, transistors, etc.) are distinct, software generations are broader and often overlap.

Generation Name Description Examples
1GL Machine Language Binary code directly executed by the CPU. No translation needed. 11001010 00001111
2GL Assembly Language Mnemonic representation using an assembler. MOV, ADD, JMP (x86, ARM)
3GL High-Level Language Procedural and structured languages. Portable across architectures via compilers/interpreters. Python, C, Java, FORTRAN, COBOL
4GL Domain-Specific Language (DSL) Languages designed for a specific application domain. Often used for database queries or report generation. Focus on what to do, not how. SQL (databases), MATLAB (math), R (statistics), HTML/CSS (markup)
5GL Constraint / Logic / AI Languages that use constraints and logical rules to specify problems, where the system "figures out" the solution. Focus on constraints, not steps. Prolog, Lisp, Mercury, some modern declarative DSLs.

7.6 Compiled vs. Interpreted vs. Just-In-Time (JIT)

How does your high-level code turn into machine code? The translation strategy has a massive impact on performance, portability, and development speed.

7.6.1 Compiled Languages

7.6.2 Interpreted Languages

7.6.3 Just-In-Time (JIT) Compilation (The Hybrid)

How does Python fit? Python source code is first compiled by the Python interpreter into Python bytecode (saved in .pyc files). This bytecode is then executed by the Python Virtual Machine (PVM). Traditionally, the PVM interprets the bytecode line-by-line, making Python slower. However, modern Python implementations (like PyPy) include a JIT compiler, and CPython (the standard version) is gaining a JIT to boost performance significantly.

7.7 Programming Paradigms

A programming paradigm is a fundamental style of programming. Languages often support multiple paradigms.

7.7.1 Procedural Programming (Imperative)

7.7.2 Object-Oriented Programming (OOP)

7.7.3 Functional Programming (Declarative subset)

7.7.4 Declarative / Logic Programming

7.8 Typing Systems: Static vs. Dynamic, Strong vs. Weak

When learning Python, it's crucial to understand how it handles types compared to others.

Python is: Dynamically and Strongly typed.

7.9 Summary Table

Aspect Key Concept Real-World Example
Lowest Level Machine Code (1GL) 10111000 00000101
Human-friendly Low Assembly (2GL) MOV EAX, 5
Efficiency & Control Compiled Languages C, C++, Rust
Rapid Dev & Portability Interpreted Languages Python, Ruby
Performance + Portability JIT Compiled Java, C#
Step-by-step logic Procedural Paradigm C, Pascal
Modeling Entities OOP Paradigm Java, Python, C++
Pure functions Functional Paradigm Haskell, Lisp
What-to-do, not how Declarative Paradigm SQL, Prolog
Type safety Static/Dynamic Typing Static: C; Dynamic: Python

7.10 Quizzes

Quiz 1: Generations & Fundamentals

1. What is the lowest-level programming language that a CPU can directly execute without translation?

Answer(B) Machine Language

2. Which generation of programming language primarily uses mnemonics like ADD, SUB, and JMP?

Answer(B) 2GL (Assembly Language)

3. FORTRAN and COBOL are examples of which generation of languages?

Answer(C) 3GL (High-Level Languages)

4. SQL (Structured Query Language) is typically categorized as a:

Answer(B) 4GL Domain-Specific Language

Quiz 2: Compilation, Interpretation, and JIT

5. Which of the following languages typically compiles directly to a standalone machine-code executable?

Answer(C) C

6. Java compiles source code into an intermediate format called:

Answer(B) Bytecode

7. A JIT (Just-In-Time) compiler is unique because it:

Answer(C) Compiles bytecode into machine code at runtime, optimizing based on usage

8. The Python standard interpreter (CPython) primarily:

Answer(B) Interprets Python bytecode on the Python Virtual Machine

Quiz 3: Paradigms and Typing

9. Which programming paradigm focuses on pure functions and avoids mutable state?

Answer(C) Functional

10. Which paradigm encapsulates data and behavior using classes and objects?

Answer(C) Object-Oriented

11. Which paradigm describes the process as a sequence of steps, using loops and conditions?

Answer(B) Procedural

12. What happens when you try to run "Hello" + 5 in Python (which is strongly and dynamically typed)?

Answer(B) It results in an error (`TypeError`)

13. Which of the following languages is statically typed?

Answer(C) Java

Quiz 4: Historical Languages and Features

14. Which language was specifically designed for business data processing with a verbose, English-like syntax?

Answer(C) COBOL

15. ALGOL was historically significant because it introduced:

Answer(B) Block structure (`begin...end`) and structured programming

16. Which of the following is a 5th Generation (constraint/logic) programming language?

Answer(C) Prolog

7.11 Exercises

Exercise 1: Language Classification Matrix

Instructions: Classify the following languages based on: (a) Generation, (b) Typical Translation Method (Compiled/Interpreted/JIT), (c) Primary Paradigm, and (d) Typing System (Static/Dynamic, Strong/Weak).

Language Generation Translation Paradigm(s) Typing
Python
C
Java
SQL
x86 Assembly
Prolog
Answers - **Python**: 3GL, Interpreted, Multi-paradigm (Procedural, OOP, Functional), Dynamic/Strong. - **C**: 3GL, Compiled, Procedural (Imperative), Static/Strong. - **Java**: 3GL, JIT (Compiled to Bytecode), OOP (class-based), Static/Strong. - **SQL**: 4GL (DSL), Interpreted (by DB engine), Declarative, Weak/Static (implicit). - **x86 Assembly**: 2GL, Assembled, Imperative, Static (Type is just bit-width). - **Prolog**: 5GL, Interpreted/Compiled (varies), Declarative (Logic), Dynamic.

Exercise 2: Machine Code to Assembly to C

Instructions: Given the following simplified x86 machine code bytes (in hex), answer the questions.

B8 0A 00 00 00 (This means: Move the value 10 into the EAX register). BB 05 00 00 00 (Move the value 5 into the EBX register). 01 D8 (Add EBX to EAX).

  1. Write the equivalent Assembly language code for these three instructions.
  2. Write the equivalent C language code for these three instructions (assuming int variables).
  3. Why is the C code shorter to write than Assembly, even though it does the same thing?
Answers 1. Assembly: ```assembly MOV EAX, 10 MOV EBX, 5 ADD EAX, EBX ``` 2. C code: ```c int a = 10; int b = 5; a = a + b; ``` 3. C is shorter because it abstracts away the specific register names (EAX, EBX). The compiler automatically chooses which registers to use and handles the memory allocation for variables.

Exercise 3: Paradigm Identification

Instructions: Read the following short code snippets and identify which programming paradigm(s) they represent (Procedural, OOP, Functional, Declarative). Justify your choice.

Snippet A (Python):

def factorial(n): if n <= 1: return 1 else: return n * factorial(n-1)

Identify & Justify: ________________________________

Snippet B (Python):

class Car: def __init__(self, make, model): self.make = make self.model = model def start(self): print(f"{self.make} {self.model} is starting.") my_car = Car("Toyota", "Camry") my_car.start()

Identify & Justify: ________________________________

Snippet C (SQL):

SELECT product_name, price FROM inventory WHERE quantity > 10 ORDER BY price DESC;

Identify & Justify: ________________________________

Snippet D (Python):

numbers = [1, 2, 3, 4, 5] squared = list(map(lambda x: x**2, numbers))

Identify & Justify: ________________________________

Answers - **A**: **Functional** (Recursive, uses pure function with no side effects, though it could also be considered procedural in Python, the recursion leans functional). - **B**: **Object-Oriented** (Encapsulates data (make, model) and methods (start) within a class). - **C**: **Declarative** (Specifies *what* data to retrieve, not *how* the database should filter or sort it). - **D**: **Functional** (Uses `map` with a lambda function to transform a list without modifying the original `numbers` list).

Exercise 4: Timeline of Languages

Instructions: Create a timeline of the major programming languages mentioned in this tutorial (Machine/Assembly, FORTRAN, ALGOL, COBOL, C, C++, Java, Python). For each, list:

  1. Decade of creation.
  2. Creator/Institution.
  3. Primary original purpose.
  4. One enduring impact on modern computing.

7.12 Homework Questions

Answer the following questions in complete sentences. Each response should be 3–5 sentences unless otherwise specified.

Short Answer Questions

1. Explain the difference between a compiler and an interpreter. Which approach is used by the standard version of Python (CPython), and what is the consequence for execution speed?

Sample AnswerA compiler translates the entire source code into standalone machine code before execution, resulting in fast runtime but slower development cycles. An interpreter executes source code line-by-line at runtime, offering faster development and portability but slower execution. Python's standard CPython implementation compiles source code to bytecode and then interprets that bytecode in the Python Virtual Machine, which makes it slower than fully compiled languages like C.

2. Why is Assembly language (2GL) still used today when high-level languages are much easier to write?

Sample AnswerAssembly is still used in situations requiring maximum control over hardware, such as in operating system kernels, device drivers, and embedded systems with severe memory constraints. It allows programmers to optimize critical performance hot-spots exactly and access CPU features that high-level languages might not expose. Additionally, security researchers use assembly to reverse-engineer malware because they must understand exactly what the machine code does.

3. Describe one key difference between the procedural paradigm and the object-oriented paradigm. Give a real-world scenario where OOP would be a better choice than procedural programming.

Sample AnswerProcedural programming focuses on a sequence of steps and functions that operate on data, while OOP organizes code around objects that bundle both data and behavior. OOP is better for modeling complex real-world systems, such as a banking application where you have `Account` objects with properties (balance, owner) and methods (deposit, withdraw) that naturally map to the domain. This encapsulation and inheritance make large codebases easier to maintain and extend.

4. What is the difference between a 4GL (Domain-Specific Language) and a 3GL (General-Purpose Language)? Provide an example of each and explain why a 4GL might be preferred for its specific domain.

Sample AnswerA 3GL is a general-purpose language designed for a wide variety of problems, like C or Python, requiring the programmer to specify the algorithm in detail. A 4GL is a domain-specific language like SQL designed to solve problems in a narrow domain, allowing the programmer to declare *what* they want without specifying the procedural steps. SQL is preferred over writing a data retrieval algorithm in C because it abstracts away the complexities of indexing and query optimization, allowing the database engine to determine the most efficient execution plan.

5. Why is Python considered a "multi-paradigm" language? Give an example of how you could write code in Python using two different paradigms discussed in this tutorial.

Sample AnswerPython is multi-paradigm because it supports procedural, object-oriented, and functional programming styles within the same language, giving developers flexibility. For example, you could write a procedural script using only loops and functions, define a class to use object-oriented design for a complex data model, and use `map`/`filter` with lambdas to process lists in a functional style—all in the same file.

Essay Questions

Answer the following questions in 300–500 words each.

6. Trace the evolution of programming languages from the 1940s to the present day. How did the shift from 1GL to 3GL (and beyond) change the nature of software development and who could become a programmer?

Suggested outline:

7. Compare and contrast compiled languages (like C), JIT-compiled languages (like Java), and interpreted languages (like Python) in terms of performance, development speed, portability, and use cases. Which type of language is best for a system-level operating system kernel, and which is best for a rapid data science script, and why?

Suggested outline:

Research Questions

These questions require additional research beyond the tutorial content.

8. Research the history of the ALGOL programming language. Why is it considered one of the most influential languages, even though it was not commercially successful? How did ALGOL-60 influence the design of C, Pascal, and even modern Python?

9. Research the concept of "Turing Completeness" (briefly introduced in Tutorial 4). Are all programming languages Turing Complete? What does this imply about the theoretical capabilities of languages like C, Python, and even SQL? Provide specific evidence for your answer.

10. Research the development of Rust or Go. Why were these modern compiled languages created? What specific problems with C and C++ (e.g., memory safety, concurrency) were they trying to solve, and how do they fit into the landscape of programming languages today?

Previous | Tutorial index | Next