Return to home
"I'm gonna go code some stuff"

What does this actually mean? What do we mean when we say "code"? Code as a verb has existing since the 1800s, but obviously in the sense of writing computer programs, it's fairly recent.

But what we mean when we say "code" in the context of computers is typically Source code. Source code is the code from a source. And that source is YOU!

When we want to write a computer program, we want to tell the computer exactly what it has to do. We don't speak in ones and zeros, we speak in language. So people have invented programming languages to allow us to express our commands without having to learn to speak computer. It is important to remember that the purpose of a programming language is to help you understand what is happening, not the computer. The computer has no idea what your foolish spoken language means, it must have instructions that it understands. beep beep bop boop.

Code must be exact

Computers are in charge of some pretty serious and dangerous stuff. So when we write code, we need it to be in a precise form. That is, there can be only one meaning for the commands we give it. Imagine a computer that works to fly and land an airplane. If the pilot "spoke" to it in English, he might say "Bring the plane down", and the computer might reply "Aye Aye, Detaching wings". This is certainly going to bring the plane down, but not how the pilot would like.

To that end, the language we are going to use is going to have exactly one meaning for everything that is written. It cannot be subject to interpretation based on feelings or deeper context. This means that what we write is going to be a limited form of English. Not all programming languages are based on English, but most of them are. The programming language we will use is called the D programming language. The reason I use this language is because it's easy to learn at the beginning, and it can be used in the most complex code you might ever see. This means we can write whatever program you want, be it simple or complex, and you don't have to switch languages. D is also very similar to many other mainstream languages, such as C/C++, Java, JavaScript, C#, and others. What you learn in this course will apply to just about every other programming language that exists, they are all very similar.

What does code look like?

import std.stdio;

void main() {
    writeln("Hello, World!");
}

The above code is a complete program. Note that it's just text in a file, it doesn't actually do anything. This is what source code looks like. In order for the computer to execute the code, it needs to be changed into the form that the computer understands. For the D language, the tool that does this is called the compiler. Like a chef uses a recipe to make food, the compiler uses the source code to make an executable binary.

You can see that source code is pretty readable. There are some normal words and some odd ones, and a lot of punctuation. I'll go over each of the pieces of the program and briefly explain what they are. It is not important at this point that you fully understand the meaning of everything, so don't worry if something is confusing. As you learn to write your own programs, the meaning will be clearer.

Imports

import std.stdio;

The import statement at the top tells the compiler that it will have to go look at some other source code to understand what we are talking about. An import statement starts with the word import, and what follows is a module that will contain definitions for the things we are about to use. In this case, the std package contains all the modules that are part of the core D programming language. The stdio module contains definitions for doing input and output from files and the console.

The final thing on the import statement is a semicolon. D uses semicolons to help the compiler know when a statement is ending. Some languages use the end of a line to denote the end a statement, but D does not. You are going to spend a lot of time forgetting to put semicolons on things, and you might begin to wonder why they are necessary. The point of them is to give the compiler stopping points so it can know what you mean. Imagine a book written without any periods or question marks. It would be difficult to read, and even more ambiguous than English normally is! Being more precise, programming languages need more punctuation to help ensure that it knows what we mean.

The Main function

void main() {
    ...
}

The next part of the program is the main function. This is a special function in D that denotes the start of your program. It isn't too important right now to understand everything about this syntax, but what is important is that every D program must contain exactly one main function.

The void word before it tells the compiler what the function gives as output. void means nothing, so the main function doesn't give anything back in return.

After the word main, you see a set of parentheses, and an opening curly brace. The parentheses tell the compiler that main is a function, and the opening curly brace defines where the function starts. We will learn more about functions in a later lesson.

Now, let's jump ahead to the end of the file, and see the closing curly brace. In D, curly braces, parentheses, and brackets all have to match, which means if you have an opening thing, you have to have the closing thing that matches. Everything between the two things is considered one whole piece. In this case, everything between the two curly braces is the body of the main function.

The body of main

writeln("Hello, World!");

Inside the curly braces is all the code the program will run. Here is where we will write most of our code during the class. In the case of this simple program, we are doing one thing -- telling the computer to write "Hello, World!" to the screen. The word at the beginning, writeln is called an identifier. It represents something to the compiler that was defined before. Can you guess what it does? It is a function, that writes whatever you tell it to the screen, and then moves to the next line. You can read the word as "write line", but as coders often do, it has been shortened to take less time to type.

The writeln function is called by using parentheses after the identifier. A function call is different than a function definition. In this program, we defined the main function, and called the writeln function. The differences on which is which depend on the punctuation and identifiers around the function. We will learn a lot more about functions soon. But what you need to know from this is, whatever you put inside the parentheses, the compiler will try to display on the screen.

Inside the parentheses, you see the text "Hello, World!". Any time you want to refer to a piece of text literally, you need to use double quotes to surround it. What would happen if you didn't have the double quotes? The compiler would think you are talking about some identifier named Hello and some other identifier named World!. Instead we just want the compiler to write the literal text "Hello, World!" to the screen, which is exactly what it does.

And finally, the semicolon ends the statement. Even though there is nothing else but a closing curly brace, you still must end with the semicolon.

Dealing with Errors

When you make a mistake in your program, the compiler may be confused, and will tell you that something is wrong. These notes are called Compile Errors and will prevent the compiler from giving you a runnable program. The compiler tries its best to tell you where it is confused, and often times will give you a hint on how to fix it. Sometimes these hints are easy to understand, and sometimes they are really really confusing!

As an example, if we remove the import std.stdio; line from the top, and compile we get the following error:

helloworld.d(4): Error: `writeln` is not defined, perhaps `import std.stdio;` is needed?

What a nice easy message! It just tells you what to do to fix the problem. Note that the error begins with the filename that contains the error, and tells you the line that contains the error (4). This helps you locate the place where the compiler is having trouble.

But not all error messages are so nice. What if instead, we accidentally added an extra curly brace:

import std.stdio;

void main() { {
    writeln("Hello, World!");
}

The error isn't as nice:

helloworld.d(6): Error: found `End of File` when expecting `}` following compound statement

What happened is that it compiled the program fine until it got to the end, and it didn't see a matching closing brace for the extra opening brace. The confusing part here is that the line number (6) is far away from the real error on line (3). But reading these kinds of errors still are not too bad.

But you will encounter very very confusing error messages. Messages that make very little sense. When this happens, pay close attention to the line number. If you are using VSCode, maybe it can help you identify where it sees problems in your code (with red squiggle underlines). You may have to look back at a previous line where you forgot something or added an extra something.

In my experience, the most common error is forgetting a semicolon. This can cause very weird errors, because the compiler thinks you are still writing the same statement from the previous line! Let's remove the semicolon from the import statement, and see what happens:

import std.stdio

void main() {
    writeln("Hello, World!");
}
helloworld.d(3): Error: `;` expected
helloworld.d(3): Error: function declaration without return type. (Note that constructors are always named `this`)
helloworld.d(3): Error: no identifier for declarator `main()`

Yikes! that's a lot of weird stuff. However, the important thing is in the first error. The compiler reads your files in order, so if there is an error on one line, it might be confused for the rest of reading your file. So it is always important to focus on the first error before looking at all the other messages. Fix that one, and all the others might go away.

But wait, it says the missing semicolon is on line (3)? Why? Because on line 3, it still thinks you are writing the import statement! Really, the semicolon belongs on line 1. But the compiler didn't know that you missed it until it started seeing things on line 3 that didn't make sense.

Variables

A variable is a piece of data that varies. The computer can store many many many many variables in its memory. In each variable, you can store one thing. But we need to be able to refer to this thing later! So we have to give the variable a name. In source code we can declare a variable by telling the compiler the variable's name, and what kind of thing we want to store in it.

The kind of thing is called a type. And the syntax we use is to first write the type, and then write the name of the variable. Finally, we can give the variable a value by assigning a value to that variable. The first two types we will learn about are called int and string. An int is an integer type, which is a negative or positive whole number. If we wanted to declare an int variable named intVar and assign it the value 42, it looks like this:

int intVar = 42;

This tells the compiler to reserve some memory to store an integer with the value 42. And it also tells it that whenever we want to look at that memory, we will use the name intVar. What can we do with this variable? Why don't we display it using writeln!

import std.stdio;

void main() {
    int intVar = 42;
    writeln("intVar = ", intVar);
}

So I snuck in a new concept here. writeln is a function that can take many things to print. In this case, we gave it two things, the text "intVar = " and the variable intVar. It prints both of them on the screen, and then moves to the next line. You can pass as many things as you want to writeln to print, you just separate them with a comma.

A string is another type that we can use. It's an odd name, but it means a string of characters. In fact, the text "Hello, World!" is a string, as is the text "intVar = ". However, using variables, we can store them for later use with a different identifier:

import std.stdio;

void main() {
    string msg = "Hello, welcome to D class!";
    writeln(msg);
}

Here, I named a variable msg, with the type string, and assigned it the value "Hello, welcome to D class!". Then when I used writeln, I gave it the identifier msg to refer back to that same variable. The compiler will write the text "Hello, welcome to D class!" to the screen, not the text "msg".

It's important to note that in D, identifiers are case sensitive. Therefore, if I name a variable msg, I cannot refer to it later using the name Msg. That would be a different identifier!

Variables can be reassigned to another value of the same type. Therefore, it's possible to assign a new value to intVar or msg, as long as I assign a number to the first, and a string to the second. Then I can refer to that new value using the same variable name. You will be using a lot of variables in code, it is the way we keep the state of the program.

Exercise

Here is a program that is missing the arugments to writeln! I'm using variables here to store values of various things. Define your name and your age by giving values to the variables name and age. Then, inside the parentheses of the writeln call, put things in the right order so you get the message:

Hello Steve, it's nice to meet you! I see you are 42 years old!

With, of course, your name instead of mine, and your age instead of the number that may or may not be my age. Remember that you pass multiple arguments to writeln by separating them with a comma.

import std.stdio;

void main()
{
    int age = 42;
    string name = "Steve";
    string greet = "Hello ";
    string pleasantry = ", it's nice to meet you! I see you are ";
    string suffix = " years old!";
    writeln(  );
}

Try working it out for yourself in VSCode, or on run.dlang.io. If you can't figure it out, you can

Return to home

©2023 Steven Schveighoffer