What is a program?

Reading time5 min

In brief

Article summary

In this article, we explain what a program is, and introduce the notion of programming languages and their specificities.

Main takeaways

  • A program is a series of instructions in a given syntax that realize an algorithm.

  • The programming language constrains the syntax of the program.

  • Choosing a programming language for a project depends on many factor, as each language has its specificities.

  • Some programming languages are interpreted, and some are compiled, to be able to be executed by the machine.

Article contents

1 — Programs and programming languages

A program is a translation of an algorithm in a particular language, that enforces some strict syntactic rules. A program that has a valid syntax according to the rules should be understandable by a computer. Therefore, the computer will be able to realize the computations described by the program (which describes the algorithm).

The syntax is defined by the programming language. There are hundreds of programming languages in the wild, each with its own specificities. During your scholarship, you will learn a few of them, which are pertinent for the associated tasks. In particular, you will learn:

  • Python – This is a great language for quickly writing a program. It has a very simple syntax, which makes it easy to learn. In addition, it has a large community, and a lot of libraries to realize standard operations. In particular, it has become the main language for everything related to artificial intelligence. This language will be the language of choice for this semester.

  • Java – Java is a convenient language for writing complex applications, due to its strict typing and object-oriented nature. It is very present in industry, and often required to be known by many companies when hiring. You will study it during the next semester.

  • HTML/PHP/CSS – These are essential languages for writing Web-based applications. You will learn about them later in your scholarship.

  • SQL – This language allows you to address a database, to add entries or perform request on it. Again, you will study it later when you learn about databases.

Depending on your cursus, you will also learn about other languages, that are adapted to the specificities of your formation.

To choose a programming language for a project, there are many criteria. Here are some of the main ones:

  • Adaptedness – Is the language adapted to your project?

  • Performance – Is the language performant for your project?

  • Libraries – Does the language offer some libraries adapted to your project?

  • Easiness – Is the language easy to learn?

  • Community – Is there an active community on the language?

  • Long-term – Will that language only be useful for this project, or will you acquire long-term skills?

  • Context – Are you working in a company that already has some codes in a particular language?

  • Demand – Does knowing that language increase the quality of my resumee?

There are obviously many other criteria.

Also, you can find a lot of pages online that rank languages. Here is an example, another one, and again another one. Obviously, depending on who writes the article and their criteria, the ranking will change. However, checking these kinds of rankings is interesting to evaluate the market demand for a language.

Information

The choice of a language has always be a dividing question among computer scientists. However, with the recent rise of artificial intelligence-based tools such as GitHub Copilot or ChatGPT, it has become pretty easy to translate a program from a language to the other.

Therefore, what is important is to master a programming language, and to understand its strengths and limitations. Transitioning to a new language will then be a lot easier, when needed.

2 — From source code to programs

Machines speak their own (abstruse) language, which is difficult to manipulate as a developer. As humans, we speak natural languages that are too complex for machines, at least for now. Programming languages are located in-between natural and machine languages. With special training, they can be easily managed by humans and, being strict and formal enough, they can be automatically translated into machine languages.

Programming languages may be discriminated based on the way they are in fine translated into machine executable statements. They can be interpreted or compiled. In both cases a tool is needed to perform the translation. As illustrated in the figure below, the first case relies on a so-called interpreter that on-the-fly translates statements written in the concerned programming language into machine code. Code written in such languages, which is the case of Python, cannot be executed without a specific interpreter. Oppositely, compiled languages rely on a compiler to translate your code in a program that can be then directly executed by your host operating system. So, wheras code written in an interpreted language can be executed on any type of machine, thanks to the interpreter, the result of the compilation of your code is directly executable but on the host machine only.

When you submit source code for execution, the Python interpreter goes through several sequential steps to check the validity of your code and translate it into machine language. These steps are as follows:

  1. Lexing – To split each line of code into tokens, which are abstract elementary units of code.

  2. Parsing – To structure sequences of tokens into a tree structure (Abstract Syntactic Tree) that indicates how to group tokens and in which order they have to be executed. At this step, the parser detects syntactic errors corresponding to unexpected sequences of tokens.

  3. Compiling – To translate your readable source code into byte code that is faster to execute and that is adapted to the execution platform (i.e., your operating system). The result of this translation is stored in (hidden) file having the .pyc extension.

  4. Executing – To execute machine specific statements to run byte code instructions. It is performed by a central element of the Python interpreter, the “Python Virtual Machine (PVM)”. As an intermediate between the machine and your code, the PVM executes your compiled source code that has been completed with its dependencies, code coming from other available libraries.

3 — Programming errors

Programming is an incremental process, meaning that the code has to be built step-by-step, each step has to be tested before moving to the next one. Development time may be roughtly divided into three iterative phases:

  • Coding.
  • Debuging.
  • Documenting.

And debugging definitely takes up a lot of development time. An important skill to acquire is to efficiently identify the source of bugs and to fix them. Many tools have been conceived to help you quickly determine where programming errors and bugs are located. But before discovering such specific tools, it is mandatory to learn how to interpret error messages rendered by the interpreter (resp. compiler).

The Python interpreter may detect errors during the interpretation of your code that are mainly of two types.

3.1 — Syntactic errors

Syntactir errors are located in the text of your code, and can be detected without executing the code (during the parsing/compilation phases). They are quite explicitly explained by the interpreter as it provides you with a description of the error and the line where it occurs.

Example

Consider the following program:

# The string to manipulate
s = "IMT Atlantique"
print(s)

# Loop to append to a new string
result = ""
for i j in range(len(s)):
    result = s[i] + result

# Print the result
print("Reversed string:", result)
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */
public class Main {

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // The string to manipulate
        String s = "IMT Atlantique";
        System.out.println(s);

        // Loop to append to a new string
        String result = "";
        for (int i j = 0; i < s.length(); i++) {
            result = s.charAt(i) + result;
        }

        // Print the result
        System.out.println("Reversed string: " + result);
    }

}

Here, the interpreter tells us that at line 7 of your code, the token j is unexepected at this position, as it cannot follow another token corresponding to variable name (i.e., i):

File "session_1.py", line 7
  for i j in range(len(s)):
          ^
SyntaxError: invalid syntax

3.2 — Runtime errors

Runtime errors appear only when the program is running (during the execution phase). They are more tricky to solve because they often depend on the context of execution. Here are a few examples that can cause such error:

  • An unauthorized operation considering the actual types of the concerned values.
  • An input/output operation on an unavailable resource (e.g., a file).
  • An access to unavailable area of the memory.
Example

Consider the following program:

# Ask user at runtime for a value
den = input("Please enter the value of the denominator")

# Perform a computation
result = 42 / den
print(result)
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */

// Needed imports

import java.util.Scanner;

public class Main {

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Ask user at runtime for a value
        Scanner scanner = new Scanner(System.in);
        System.out.print("Please enter the value of the denominator: ");
        String denStr = scanner.nextLine();
        double den = Double.parseDouble(denStr);

        // Perform a computation
        double result = 42 / den;
        System.out.println(result);
    }

}

Although syntactically correct, the third line of the following code will generate a division by zero runtime error if 0 is assigned to den.

3.3 — The case of random numbers

Sometimes, programs make use of random numbers. Consider for instance the following program:

# Needed imports
import random



# Fill a list with 10 random numbers
numbers = [random.randint(-10, 10) for i in range(10)]

# Generate an error if 0 is in the list
if 0 in numbers:
    raise ValueError("Zero is in the list")

# Otherwise, print the list
print(numbers)
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */

// Needed imports

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Create a random number generator
        Random random = new Random();

        // Fill a list with 10 random numbers
        List<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            numbers.add(random.nextInt(11) - 10);
        }

        // Generate an error if 0 is in the list
        if (numbers.contains(0)) {
            throw new IllegalArgumentException("Zero is in the list");
        }

        // Otherwise, print the list
        System.out.println(numbers);
    }

}

Sometimes, when running your program, you will get the following output:

[3, 3, -2, -4, 5, 4, -5, -2, 3, 3]

Or sometimes:

[-4, 2, -6, -10, -7, -7, 3, 3, 5, 4]

Or:

Traceback (most recent call last):
  File "session_1.py", line 9, in <module>
    raise ValueError("Zero is in the list")
ValueError: Zero is in the list

Well, that’s expected. However, imagine a more complex scenario where the toy error above is a really important error you need to avoid. It is going to be pretty hard to debug, as sometimes your program will work as expected, and sometimes it will crash due to a semantic error.

In practice, it is possible to force a series of random numbers to always be the same. Indeed, random numbers are in fact pseudo-random. They are generated using a deterministic series that has good randomness properties, which is initialized using a “seed”, i.e., a first initial value. If the seed is known, then, the entire series can be predicted. Even better, if the seed can be set to an arbitrary value, then we can enforce that all executions of the same program will lead to the same generation of random numbers.

Each random number generator provides a way to set the seed to a given value (here, 42). This can be done as follows:

# Needed imports
import random



# Set the seed
random.seed(42)

# Fill a list with 10 random numbers
numbers = [random.randint(-10, 10) for i in range(10)]

# Generate an error if 0 is in the list
if 0 in numbers:
    raise ValueError("Zero is in the list")

# Otherwise, print the list
print(numbers)
/**
 * To run this code, you need to have Java installed on your computer, then:
 * - Create a file named `Main.java` in a directory of your choice.
 * - Copy this code in the file.
 * - Open a terminal in the directory where the file is located.
 * - Run the command `javac Main.java` to compile the code.
 * - Run the command `java -ea Main` to execute the compiled code.
 * Note: '-ea' is an option to enable assertions in Java.
 */

// Needed imports

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Main {

    /**
     * This is the entry point of your program.
     * It contains the first codes that are going to be executed.
     *
     * @param args Command line arguments received.
     */
    public static void main(String[] args) {
        // Create a random number generator
        Random random = new Random();

        // Set the seed
        random.setSeed(42);

        // Fill a list with 10 random numbers
        List<Integer> numbers = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            numbers.add(random.nextInt(11) - 10);
        }

        // Generate an error if 0 is in the list
        if (numbers.contains(0)) {
            throw new IllegalArgumentException("Zero is in the list");
        }

        // Otherwise, print the list
        System.out.println(numbers);
    }

}

Let’s run our program three times again

[10, -7, -10, -2, -3, -3, -6, -7, 7, -8]
[10, -7, -10, -2, -3, -3, -6, -7, 7, -8]
[10, -7, -10, -2, -3, -3, -6, -7, 7, -8]

As you see, the result is always the same. Now, what you can do to debug your progam is to find a value of the seed that leads to an error. Then, keep that seed set and try to see what happens with this particular problematic random configuration!

Important

A few important remarks:

  • The list you obtain may be different from a computer to another, as the random number generator used may be different. Setting the seed only guarantees reproducibility on a single machine.

  • Each random number generator comes with its own seed. If you use functions from library random and numpy.random for instance, you will have to set the seeds of the two generators to guarantee reproducibility.

  • You cannot use the random seed to change the performance of your programs. For instance, if your algorithm that incorporates random elements solves a problem better with seed = 42 than it does for seed = 0, it is unfair to report performance obtained for the first case. Indeed, it just means that 42 leads to a lucky initialization of your algorithm for this particular problem instance, but it will not be the case in general. Reporting performance for random algorithms should always be done by running the program numerous time and providing averages and confidence intervals!

To go further

Important

The content of this section is optional. It contains additional material for you to consolidate your understanding of the current topic.

To go beyond

Important

The content of this section is very optional. We suggest you directions to explore if you wish to go deeper in the current topic.

  • name.

    Short description.