Variables Are the Spice of Life

This is the third entry in my Lua Tutorial series. See here for an introduction for this series, or here for our previous entry covering “Hello World”.

We can print to the screen now and see all the fun math operators Lua has, but we really can’t do anything of use yet. We’re going to cover the basics of variables and getting user input so we can actually start doing some fun stuff with Lua. After all, what would be the point of programming if everything were static?

Variables in Lua are dead easy to work with. You basically have variables and the type is decided by the value. If we look here we can see that: “Lua is a dynamically typed language. Variables do not have types; only values do. All values carry their own type. Therefore, there are no type definitions in the language. … There are seven basic types in Lua: nil, number, string, function, CFunction, userdata, and table. Nil is the type of the value nil, whose main property is to be different from any other value. Number represents real (floating point) numbers, while string has the usual meaning.”

In short, this works out to mean, you don’t really need to care what the variable is, only what is in it (this is extremely important). You can treat basically all variables the same as long as you properly handle them based on value and similar. Setting up a new variable is also dead easy in Lua.

variable = "thing"

Or:

variable = 1
variable = nil
variable = {}
variable = function () return 0 end

You can also set a variable as local to describe that it is only relevant in the code block it is found in, but this will be covered more later.

In order to read input, we want to use: io.read() (which can be loaded with parameters, but we’ll get to that in a bit). Let’s get a name and display it on the screen. I am going to omit the file name from here on out, feel free to name this whatever you would like as a test and run it as previously discussed. I am also going to skip the shebang, so make sure to add that as the first line if you are using Linux or macOS (#!/usr/bin/lua5.1). I’ll introduce the code first, then we’ll go ever exactly what we’re doing here.

io.write( "Who are you? " )
name = io.read()
io.write( "It's great to meet you " .. name .. "\n" )

When we run this, we get an interaction like follows:

./variables.lua 
Who are you? Some Dude
It's great to meet you Some Dude

So, we added a few things there. io.write( [stuff] ) allows us to write directly to stdout (standard output, A.K.A. the console where you’re running this [There’s more than this, but we won’t get into that yet]) in a more literal fashion. We use .. to concatenate strings. What this means is, we just add our two strings together so “A” .. “B” would be “AB”. If you notice, in the second io.write, we concatenate a “\n” at the end which tells it to add a newline (which is included with print()). We use name = io.read() to get a standard line (and cut off the newline) into a variable we initialize called name. So, we can grab a string, but is that all we can do?

Of course not! The Lua documentation lists what io.read() can take here. We basically end up with the following:

"*all"	reads the whole file
"*line"	reads the next line
"*number"	reads a number
num	reads a string with up to num characters

These can further be shortened down to:

*a
*l
*n
num

Let’s get some numbers and work with them in our next example.

io.write( "Give me a number: " )
a = io.read( "*n" )
io.write( "Give me another number: " )
b = io.read( "*n" )
io.write( "We have a+b=" .. a + b .."\n" )

If we run this, we get something like follows:

Give me a number: 1
Give me another number: 1
We have a+b=2

What happens if we fail to feed it a number?

Give me a number: 1
Give me another number: not a number
/usr/bin/lua5.1: ./variables.lua:11: attempt to perform arithmetic on global 'b' (a nil value)
stack traceback:
	./variables.lua:11: in main chunk
	[C]: ?

We get an error message because we’re trying to add something which is a number to something which is not (your line number after the colon will vary, I’m adding this to a test program so the number is pretty arbitrary). We can’t really add a number to a non-number so Lua doesn’t really know what to do with this so it fails. You can test for what variable you have however via type( [variable] ).

Let’s try this out and see what we get.

io.write( "Give me a number: " )
a = io.read( "*n" )
io.write( "Give me another number: " )
b = io.read( "*n" )

io.write( "Type of a: " .. type( a ) .. "\n" )
io.write( "Type of b: " .. type( b ) .. "\n" )

io.write( "We have a+b=" .. a + b .."\n" )

And we can run this a few times and get:

Give me a number: 2
Give me another number: 2
Type of a: number
Type of b: number
We have a+b=4
Give me a number: 1
Give me another number: five
Type of a: number
Type of b: nil
/usr/bin/lua5.1: ./variables.lua:15: attempt to perform arithmetic on global 'b' (a nil value)
stack traceback:
	./variables.lua:15: in main chunk
	[C]: ?

If you notice, we get a nil value on the second input. That’s because we are trying to look for a number and not getting one. If we tweak our input we can see something like follows.

Give me a number: 1
Give me another number: 5five
Type of a: number
Type of b: number
We have a+b=6

We didn’t put in a number, but we had a number at the beginning so we ended up with the rest being cut off. This is pretty common practice with most dynamically typed languages. Lua is a little more limited than something like Perl here, but you can work around these types of issues with if statements. There are more variable types and way more things we can do, but we are not going to get into that yet.

Here’s our summary or our content for advanced learners:
Lua is a dynamically typed language.
Lua defines a variable as [name] = [thing].
Variables are defined by their values rather than a type.
We can get the type via type( [variable] ).
We can use io.read() to get various values.

"*all"	reads the whole file
"*line"	reads the next line
"*number"	reads a number
num	reads a string with up to num characters

These can further be shortened down to:

*a
*l
*n
num

We can use io.write() to print without a new line unless defined via “\n” or print() to print content with a new line.
We can use if statements long term to sort all of this out.
We can combine all of this via:

io.write( "Who are you? " )
name = io.read()
io.write( "It's great to meet you " .. name .. "\n" )

io.write( "Give me a number: " )
a = io.read()
io.write( "Give me another number: " )
b = io.read()

io.write( "Type of a: " .. type( a ) .. "\n" )
io.write( "Type of b: " .. type( b ) .. "\n" )

io.write( "We have a+b=" .. a + b .."\n" )

We can get the following output:

Who are you? Some Dude
It's great to meet you Some Dude
Give me a number: 1
Give me another number: 1
Type of a: number
Type of b: number
We have a+b=2
Who are you? Some Dude
It's great to meet you Some Dude
Give me a number: 1
Give me another number: five
Type of a: number
Type of b: nil
/usr/bin/lua5.1: ./variables.lua:15: attempt to perform arithmetic on global 'b' (a nil value)
stack traceback:
	./variables.lua:15: in main chunk
	[C]: ?
Who are you? Some Dude
It's great to meet you Some Dude
Give me a number: 1 
Give me another number: 5five
Type of a: number
Type of b: number
We have a+b=6