Variables are a requirement for us to be able to write programs that can do something useful. So let’s try to get an understanding of what they are. But before that, let’s write a bit of code as it’s easier to reason about something you’ve at least seen before.

First, let’s rewrite the program we learned in the last lesson, but using a variable to hold the greeting. Remember to create a new directory and inside it to create a new file names something that ends in .odin. In this file, write the following code.

1package main
2
3import "core:fmt"
4
5main :: proc() {
6    greeting := "Hellope!"
7    fmt.println(greeting)
8}

Save the file and try to run it with odin run .. If you get any errors, go back and fix them and try again until you get it right. Take note of the output. Compare it to the output of the Hellope! program in the previous lesson. While you are working on this program, you may want to try to change the value of the variable, just to make sure that everything works as you would expect.

That was so simple that we might as well write another little program. This one adds to numbers together and prints out the sum. Variables are used to store the numbers as well as the sum.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "core:fmt"

main :: proc() {
    num1 := 5
    num2 := 7
    sum := num1 + num2
    message := "The sum is:"

    fmt.println(message, sum)
}

Again, once you have run this program, you could try changing the value of the variables num1 and num2 and see that in each case, the program behaves like you would expect. You could even change the operator from plus to subtraction (-), multplication (*) or division (/). Of course, the variable name sum isn’t not be appropriate with the other operators, but that’s a minor detail at this point.

If you do try the other operators, when you get to division, run one test with num1 being 0 (zero), just to see what happens when you try to run this program. Don’t hesitate to try things out and to take note of how the program behaves under different circumstances. If you get behaviors that are wrong or don’t make sense, chances are that you are going to have to come back and fix something.

Since you haven’t even broken out a sweat yet, let’s write a third little program. This one uses a variable to indicate whether or not the user knows Odin and prints a message based on the value of the variable. You will enounter a new construct that will shall look at in this lesson: the if statement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "core:fmt"

main :: proc() {
    knows_odin := true

    if knows_odin {
        fmt.println("Oh, you know Odin. That's great news!")
    } else {
        fmt.println("That's too bad. I really think you should learn it.")
    }
}

Once you have run this program and seen the output, try changing the value of the variable from true to false and run the program again. What happens? Apart from true and false, are there any other values that the variable knows_odin could possibly take?

What is a variable?

A variable is some location in the computers memory that has been reserved for a particular purpose, to hold some specific piece of information: the price of a pair of shoes, your age, the exam scores of all students in a classroom, the name of your pet, etc. It is often compared to a box or a jar with a label on it, describing the contents.

The name variable indicates that it is something that can change over time. That is, the value it holds can change.

All variables have three important properties:

  1. a type
  2. a name
  3. a value

Let’s look at what each of these properties means.

Type

You might have heard that a computers think in zeros and ones. This is not really true. First of all, computers don’t think at all. Moreover, zeros and ones are really just a way we represent two differnt voltage states that flow through differnt parts of the computer: voltage low and voltage high. If we have multiple wires that each carry either voltage low or voltage high and we represent this two states as 0 and 1, then we can combine the states of each wire to form a larger number, like 10100011, just like we can combine digits together to form a larger number in everyday math. From 5, 6 and 9 we can form 569.

The difference is that since each wire can only hold one of two states, it means that each digit in our number will consists only of two digits. This forms the basis of the binary system of counting. We can represent any number in our everyday decimal system as a binary number.

So all the memory cells and the CPU only work with voltage states, which we can think of as being the digits 0 and 1. Not a single memory cell will ever hold a letter of the alphabet, an emoji, a part of a picture, a movie or a song. Yet, we type words on our computers, we watch movies, listen to music and so on. So how does that work?

It works because the numbers inside the computer are given meaning by software that interprets them. So for instance, the number 67 might be interpreted as a capital letter “C” by a text editor, as a color value of a single pixel in a drawing program or a movie player, as one tiny slice of a sample in a music player. It is the software that provides a context in which to interpret an individual number.

In essence that is what type is. It is a way of interpreting numbers stored in the computers memory, either as numbers or possibly as something else, like text, sound or pictures.

Odin comes with quite a few types built in and makes it possbile for you, the programmer, to create new types as you need them. We are going to look into all the built-in types as well as how to make our own types in later lessons. For now, in order to keep things simple we are going to restrict our discussion to only three types, all of which have been used in the three programs above. They are: integers (int), strings (string) and booleans (bool). We’ll introduce other types gradually, as we need them.

At this point, just know that integers are numbers, without a decimal point, like 1, 411 and -73.

Strings can hold words, sentences, numbers, special characters, just about anything. You will use them to hold things like names and messages.

Booleans are a type that can only hold one of two values: true or false. As you will soon see, booleans are very useful in programming.

There is a lot more that can be said about types, but this should hopefully be enough for now. We’ll dig deeper only when we need to.

Name

Variables are given names to uniquely identify them. Just as parents normally give different names to each one of the children, so the programmer gives different names to each variable, so that they can be called individually.

Value

Every variable has a value. The possible values it can hold is restricted by the type of the variable. So an integer can hold a 2, the number 1277 or even -3. However it can’t hold the word “phone”, because that is a string, not an int.

The value of a variable can change over time. It is the only property that can. The other two stay the same.

Fine, but how does this help me?

Hopefully you have a basic understanding of what a variable is by now. But you may have noticed that by using a variable, our “Hellope!” program has actually become one line of code longer. What on earth do we get in return for that increase in size? One thing that a variable is used for is to store some information that came from outside the program.

The “Hellope!” program will always print the same message. Fine, you can go into the code and change the message, but if you don’t, every time you run the program, it will print the same “Hellope!” message. But what if you wanted a program that asked the user to enter some exam scores and calculated the average score and printed it out? You’d need to use variables to store that information.

What if you wanted to pass in the name of a file to your program and have your program analyze that file and tell you how many words the file contains? You’d need to use variables to pull that off.

What if you simply wanted to print out an appropriate greeting based on the time of the day? You’d probably store the current hour in a variable and use a conditional to determine which greeting to use. In fact, let’s give that a little try, shall we?

Getting the current hour

We want to write a program that says “good morning”, “good afternoon”, “good evening” or “good night”, depending on the time of the day. First we need to think a bit about this. What time should be associate with each greeting? And how do we get the current time of the day?

If this were a job for a client, they would normally tell you what morning, afternoon, evening and night means to them. That is what would be called their requirements. Since we are writing this for ourselves, we get to choose. However, we should probably stick to what is conventional. I’ll use the following:

  • from 4am to 12pm is morning
  • from 12pm to 5pm is afternoon
  • from 5pm to 11pm is evening
  • the rest is night

As for getting the time, there is a package called core:time that we can import (like we import core:fmt to be able to print things to the terminal window). This gives us a procedure called now that gives us the current date and time and another procedure called clock_from_time that we can use to extract time components (hour, minutes and seconds). Let’s start off by seeing if these two procedures will do what we want them to. Try the following little program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import "core:fmt"
import "core:time"

main :: proc() {
    n := time.now()
    h, _, _ := time.clock_from_time(n)

    fmt.println(h)
}

If you run this program it should print out the current hour. And by the way, the procedure clock_from_time, as we already said, returns hours, minutes and seconds, but we only care about the hours. We discard minutes and seconds by putting underscores (_) in place of variable names.

Notice how, just like in the case with core:fmt, we have to prefix each procedure from the time package with it’s name? In this case you are only typing it twice, but if this were a longer program where you had to repeat time a lot more, it might get tedious. Odin allows you to alias the package name. This would look like this.

 1package main
 2
 3import "core:fmt"
 4import t "core:time"
 5
 6main :: proc() {
 7    n := t.now()
 8    h, _, _ := t.clock_from_time(n)
 9
10    fmt.println(h)
11}

We have now aliased time to t. This can be useful, but be careful not to create a naming clash with something else. If the variable n (for now) had been t (for time), instead, you would have got errors trying to run, due to a naming clash between the package alias and the variable name.

You will want to run the program at least once during the hours between noon and midnight to see if we get the result in 12-hour or 24-hour time. I ran it on my computer at 1:24pm and got the result 13, which is perfect. That makes it much easier to work with. 12-hour time would require us to find out if it’s AM or PM and would get a bit messier.

Now that we know that the first part of the program works, let’s move on to the next part.

Conditionals

We want to print a greeting that depends on the current hour, which is held in the variable h. To do this we need the help of an if statement. We will also use if’s trusty sidekick else to help us fully accomplish our task.

You already know what “if” means. You use it regulary. “If you’re hungry, eat something”, “If it’s cloudy, carry an umbrella along”, etc. If something is the case, then do something else. As you can imagine, with programming we’re just going to have to be a bit more formal. What directly follows the if statement is a condition, which can be either true or false, never anything else. (Now you see the use of the boolean type.) After the conditional comes a block (delimted by a pair of “curly brackets” { and }), containing statements to be run only when the condition is true.

Look at the following code:

if age > 17 {
    fmt.println("You may enter")
}

We have a variable age, which holds some value, let’s image it holds 29. Then we enter a conditional which checks if age is greater than 17. Notice that this statement can only be either true or false. Try to find a single number for which this statement is either both true and false at the same time or neither true nor false. You won’t find one.

The fmt.println statement will only run if the conditon age > 17 is true (which in our case it is). Remember in the previous lesson I said that the default order is from top to bottom? Well, here is a case, where we can modify the order of execution, depending on the value of the condition. If age were something like 15, the block right after if age > 17 would be skipped.

But wait, what about if we need to print a different message if the condition is not met? This is where else comes in handy.

if age > 17 {
    fmt.println("You may enter")
} else {
    fmt.println("Sorry, you can't enter")
}

In the above code, you are guaranteed that exactly one of the two print statements is run. It will never happen that both are skipped or that both are executed. Either one or the other, depending on the current value of the age variable.

In hour program we need to go a bit further. We have four different times we need to consider. In order to do that, we can add else if statments, which is basically a way of saying “if the previous condition wasn’t true, what about this one?” We always end with an else statement, to capture anything the failed all the tests.

Let’s look at how we could build this, bear in mind that from the first part of our program, the hour is stored in a variable simply called h. If we add the conditional code directly to that program, we have to wait a long time to see that the condition changes. So we’ll write a different program, test our conditional there, and once we are happy with it, we can copy the code over to the main program.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import "core:fmt"

main :: proc() {
    h := 4

    if h >= 4 && h < 12 {
        fmt.println("Good morning")
    } else if h >= 12 && h < 17 {
        fmt.println("Good afternoon")
    } else if h >= 17 && h < 23 {
        fmt.println("Good evening")
    } else {
        fmt.println("Good night")
    }
}

I few notes about this. The && you see simply means “and”, and the >= should be read as greater than or equal to. So the first conditional reads “if the current hour is greater than on equal to 4 and less than 12”. I tried to write this code in the way that it is as readable as possible.

You could run this over and over again with different values for h (between 0 and 23) to see that it gives you the appropriate greeting in each case. If you get bored of changing the value of h, saving and running again, here is a version that will report the greeting for all the hours. I’m not going to explain the code at this time, take it a sign of things to come. (By this time I’m even convinced that you can look at the code and more or less figure out what it’s doing.)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "core:fmt"

main :: proc() {
    for h in 0..=23 {
        fmt.printf("Hour: %d ->", h)

        if h >= 4 && h < 12 {
            fmt.println("Good morning")
        } else if h >= 12 && h < 17 {
            fmt.println("Good afternoon")
        } else if h >= 17 && h < 23 {
            fmt.println("Good evening")
        } else {
            fmt.println("Good night")
        }
    }
}

After running the tests, the code seems to work just fine. The final step is to combine what we wrote previously with what we have just done. The result will look like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import "core:fmt"
import "core:time"

main :: proc() {
    n := time.now()
    h, _, _ := time.clock_from_time(n)

    if h >= 4 && h < 12 {
        fmt.println("Good morning")
    } else if h >= 12 && h < 17 {
        fmt.println("Good afternoon")
    } else if h >= 17 && h < 23 {
        fmt.println("Good evening")
    } else {
        fmt.println("Good night")
    }
}

Go ahead and try it. It may be a trivial program, but it is also dynamic. It doesn’t print the exact same message each time it is run. And if you’re new to programming, that is a big step.

Once you have a good graps of the basic building blocks, you will soon be writing longer, more complicated programs.

Final words

Now that we are able to write programs that can act on the value of a variable, we don’t want to limit ourselves to just the time of the day. We’d like to be able to act on information that the user will give us. Thta is what we will tackle in the next lesson, via command-line arguments.

Exercises

You have learned a few more things, so the exercises are gradually becoming a bit more interesting. A few more lessons and you will be able to write some useful programs. In addition to these exercises, as you become more and more familiar with Odin, you can come up with your own programs as well.

You can find the answers to the exercises here

Exercise 1

Write a program that prints out the current time (hours:minutes:seconds).

Exercise 2

Modify the following program to print out “Well then, would you like something to drink” if the user is not hungry.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import "core:fmt"

main :: proc() {
    is_hungry := false

    if is_hungry {
        fmt.println("What would you like to eat?")
    }
}

Once you have verified that it works, try changing the value of the is_hungry variable to true to make sure the program works as expected.

Exercise 3

The following program is very similar to the one in exercise 2. Notice, however, the ! before the variable in the if statement.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "core:fmt"

main :: proc() {
    is_tired := false

    if !is_tired {
        fmt.println("Good, then you can continue learning Odin")
    } else {
        fmt.println("In that case, rest a bit")
    }
}

After running the program, change the value of is_tired to true and try again. Based on the results you get, can you figure out what the exclamantion mark before the variable name does? You could remove it and see how that changes the program.

Exercise 4

To compare two integers for equality, use two equal signs (==). With this in mind, write a program that sets an integer variable called num. Then use if, if else and else to do the following:

- if the number is greater than 15, print "too large"
- if the number is 3, type "fizz"
- if the number is 5, type "buzz"
- if the number is 15, type "fizz buzz"
- if the number is less than 1, print "to low"
- for any other number, print out the number

Remember that the program will try the if statement first. Only if that condition is false will it move on to the first if else. So you will want to plan out the order in which you want to test things. Run the program several times to see that it behaves correnctly for every number from 0 to 16.

Exercise 5

Modify the time-of-day greeting program to use “good day!” sometime between “good morning” and “good afternoon”. I’ll let you decide the hours in which “good day would be appropriate. When you are done, run the program several times to test that it behaves as expected.

Exercise 6

Just like we can use && to say that two conditions must both be true (AND), we can use || to say that one of two conditions must be true (OR). Use this information to write a program that creates an integer variable called num and prints out “out of range” if the value of num is less than 20 or greater than 40. When you are done, test the program with the following values to ensure that it behaves according to specification:

- 17
- 20
- 33
- 40
- 78

Exercise 7

Write a program that current minute count in a variable. Then print “The hour just started” if minutes is less than or equal to 10 or “We are close to the end of the hour” if minutes is greater than or equal to 50. In any other case, don’t print anything.

Exercise 8

Modify the program in exercises 7 to print “We are well into the hour” if neither of the two conditions is met.

Exericse 9

The following program is meant to print “denied” if the person’s is less than 18 or greater than 65. However, the program is not working. Fix all the errors and test it to make sure that it behaves correctly for the ages: 17, 18, 19, 50, 64, 65 and 66.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import "core:for"

proc :: main() {
    age := 13

    if num <= 18 or num => 45 {
        fmt.println("Not for you")
    }
}

Notice that there are two diffent types of errors you can run into. First you have errors that cause the Odin compiler to fail. Then you have logical errors. Odin has no problem with those, it will happily run. You need to keep a keen eye on the output to ensure that the program works according to expection. Don’t rush through this exercise, make sure you have found every problem.

Exercise 10

You can use + to add to integers together. Write a program that adds the current values of hour, minutes and seconds together and prints out the result.

Exercise 11

In the previous exercise you used the + operator to add two integers. Is is possible to also add two booleans? What about two strings? Try true + false or "abc" + "def" to see if they will work. As usual, take note of the results. If you have previous experience with some other programming language python, then you could contrast Odin with that language when it comes to how + works with non-numeric types.

Exercise 12

The procedure time.clock_from_time prints out 24-hour time. Write a program that converts that into 12-hour time instead and prints it out.

Exercise 13