-
Notifications
You must be signed in to change notification settings - Fork 54
Hello, world!
Here is the traditional first program in Mercury:
:- module hello.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
main(!IO) :-
write_string("Hello, world!\n", !IO).
Save this in a file called hello.m
, then you can compile it like this:
mmc hello.m
If all goes well, it will produce an executable that you can run:
./hello
Mercury programs are organised into modules. The first line declares a
module named hello
. All declarations begin with the :-
symbol.
Modules are composed of two parts, the interface and the implementation. The second line begins the interface.
The next line imports a module called io
from the standard library,
for input and output.
The next line exports a predicate called main
.
A predicate is similar to a function or procedure in other languages.
Both of its parameters have the type io
, imported from the io
module
(the same symbol can name different things in Mercury).
We will explain the significance of di
, uo
and is det
later.
For now, it suffices to say that the first argument is input (in fact,
destructive input), the second argument is output (unique output),
and the predicate is deterministic.
The next line begins the implementation section of the module.
In the implementation section, we have the definition of the main
predicate. It is defined by a clause consisting of three parts: the head,
the neck (:-
), and the body, followed by a full stop (.
). The head
contains the predicate name, and the body makes a call to write_string
which comes from the io
module. But what are the mysterious !IO
bits?
Variables in Mercury always begin with a capital letter [A-Z] or underscore, e.g. Apple or _Banana are valid variable names.
An exclamation mark (!) followed by a variable name is called a state variable and represents two automatically numbered variables. Another way to write the clause without using state variables is this:
main(IO0, IO) :-
write_string("Hello, world!\n", IO0, IO).
It works like this: a Mercury program enters main
with a value representing the "state-of-the-world" at that point,
bound to the input variable IO0
(it has the type io
).
The call to write_string
takes IO0
, writes a string to the console,
then returns a new "state-of-the-world" -- the world in which the string
was written -- which is then bound to a variable IO
.
When main
exits, the value of IO
is returned to its caller.
To make two calls to write_string
, we could write the following.
Notice the use of comma (,) to join two calls in the body.
main(IO0, IO) :-
write_string("Hello, ", IO0, IO1),
write_string("world!\n", IO1, IO).
For three calls:
main(IO0, IO) :-
write_string("Goodbye, ", IO0, IO1),
write_string("cruel ", IO1, IO2),
write_string("world.\n", IO2, IO).
To call write_string
you must provide an io
value as input, and
you get a new value back out as output. (We will explain why in future.)
It quickly becomes tedious to number variables manually, which is why we
introduce state variables so soon. The previous predicate can be written
more conveniently:
main(!IO) :-
write_string("Goodbye, ", !IO),
write_string("cruel ", !IO),
write_string("world.\n", !IO).
Each occurrence of a state variable will be expanded out by the compiler into two normal variables, numbered in an obvious way. State variables make it much easier to read and write programs involving a sequencing of "states".
Now that we understand the basic structure of a Mercury program, how to compile and run a program, and how to get some output on the screen, we can continue.
-
Compile and run one of the programs above.
-
What happens if you change the order of the
write_string
calls without renumbering the variables, and without using state variables? -
Try passing the same "state-of-the-world" value as input to two different calls to
write_string
, what happens?