Mercury -- Trying it out for size.

λ June 21, 2019
Tags: mercury

Having spent a little time with Mercury now and started to get used to the differences between it and Prolog, I thought in this post we’d write a tiny little program to try out the various forms, functions for example, as they are specific to Mercury and not seen in Prolog.

In order to get used to “state variables” and “functions” I studied the libraries and decided that a tiny test program to start trying out some of the various features, one little step at a time, would be the way to go.

First, the output.

Let’s just dive straight in first with the build command and then run it to see what comes out:

$ mmc --make hello
Making Mercury/int3s/hello.int3
Making Mercury/ints/hello.int
Making Mercury/cs/hello.c
Making Mercury/os/hello.o
Making hello
sean in ~/tmp λ ./hello
$ ./hello
Hello, World, here's some info!
dir char   = /
parent dir = ..
this dir   = .
time thing = Fri Jun 21 15:27:57 2019

The mmc --make hello command on the first line is how we tell the Melbourne Mercury Compiler to create our file. This is the recommended way to build an application. If your project is composed of multiple files, they should be included as modules from the main. The mmc compiler is pretty slick when it comes to building and linking your application and I would say that for 99.9% of most users, the above is all you will ever need.

Once the program builds, we can then run it, ./hello and we see the output in the console. OK, that’s great, now let’s take a look at the code and then go over it and see what features we used.

:- module hello.

:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.

:- implementation.
:- import_module char, dir, int, list, string, time.

main(!IO) :-
  time(Time_T, !IO),
  Time = asctime(gmtime(Time_T)),
  format("Hello, World, here's some info!\n\
dir char   = %c\n\
parent dir = %s\n\
this dir   = %s\n\
time thing = %s\n",
        [
         c(dir.directory_separator),
         s(dir.parent_directory),
         s(dir.this_directory),
         s(Time)
        ],
    !IO).

[1] declares this as being the hello module. It must be the same name as the module file, so hello.m is the name of this source file.

[3] opens the interface section. As we need the io module to declare the main predicate, we pull that in on [4] then define the main predicate. Remember that all predicates and functions declared in the interface are the only ones visible to users of the module.

[5] During the course of a compilation run, the main predicate must be declared somewhere in one of the files. It doesn’t care where, so long as it gets done. Here we are stating that out predicate has two parameters and is deterministic. The various determinism values include: (doc source)

  • have exactly one solution, then that mode is deterministic (det);
  • either have no solutions or have one solution, then that mode is semi-deterministic (semidet);
  • have at least one solution but may have more, then that mode is multi-solution (multi);
  • have zero or more solutions, then that mode is nondeterministic (nondet);
  • fail without producing a solution, then that mode has a determinism of failure

[7] This is the start of the actual module implementation. The presence of this declaration effectively closes the implementation section for us. We are now ready to write some code and finally, [8] imports, on one line, the modules that we will be using in this little example.

When you import a module, you don’t have to use the module name as a name-space indicator but in large examples of code I have seen already, it seems that it is a convention to do so on the grounds of readability, but here, I didn’t always as I played and explored the limits of the compiler etc.

[10] is our main predicates signature declaration; we are using an IO state variable, this is expanded by the compiler, the interface stated we have two parameters, an input IO state and a final new world IO state. The ! tells the compiler to turn !IO into IO0 and IO, these representing the initial state and the final exit state of the IO world. By using !IO in the body of the code the compiler will automatically increment and use the correct variables. See here for a fuller explanation.

main(!IO) :-
  time(Time_T, !IO),
  Time = asctime(gmtime(Time_T)),

[11] makes a call into the time module, this is a predicate not a function. You can tell because we need to have the output written into Time_T. I called it that because if you are familiar with the standard C library time functions, this will all look pretty familiar.

[12] is actually two nested function calls. time.gmtime hands back the “C” tm structure and that is immediately passed into time.asctime which creates a nice printable string containing the current time and date. This is one of the things I am looking forward to in Mercury: functions! Sometimes prolog code can get a bit verbose as you have to create variables as outputs, only to use them as input to the following predicate. Erlang is also like this but no surprise as the first version of Erlang was written in Prolog, and still keeps a lot of the syntax to this day.

The following lines [13-17] were merely to test out the fact that the compiler, when it sees a terminating backslash on a line, it will ignore that backslash and then append the following line to the ongoing source text accumulation; or simply, it’s a line continuation character! Note the C.printf print format specifiers.

format("Hello, World, here's some info!\n\
dir char   = %c\n\
parent dir = %s\n\
this dir   = %s\n\
time thing = %s\n",

These lines contain the arguments to the formatted output predicate format, they are always presented as a list (the list module is not included for free, mercury is quite strict in that you have to declare everything, every time) of values.

[19] uses the c() tag to say that we are passing a character, and hence [20-22] are s() for strings.

        [
         c(dir.directory_separator),
         s(dir.parent_directory),
         s(dir.this_directory),
         s(Time)
        ],
    !IO).

I continue to learn! If you have any questions, or corrections, or anything else to add please use the comment section below.

Good day to you!

Comments