[m-rev.] [for review] Modernize website tutorial's syntax
Michael Hendricks
michael at ndrix.org
Sat Dec 31 04:54:08 AEDT 2011
The website tutorial used some old-fashioned syntax which the language
documentation suggests should not be used. Use the recommended syntax
throughout:
* Replace '__' with '.' as namespace separator
* Use state variable notation instead of DCG notation
* Use 'mmc --make' rather than 'mmake'
---
dcgs.m4 | 42 ++++++++++++++++++--------------------
hello-world.m4 | 57 +++++++++++++++++++---------------------------------
name-spaces.m4 | 18 ++++++++--------
src/facnfib.m | 18 ++++++++--------
src/hello_world.m | 6 ++--
sums-n-things.m4 | 6 -----
6 files changed, 62 insertions(+), 85 deletions(-)
diff --git a/dcgs.m4 b/dcgs.m4
index eb75eb8..aeb9617 100644
--- a/dcgs.m4
+++ b/dcgs.m4
@@ -10,8 +10,7 @@ code. DCGs are borrowed from Prolog where, as their name suggests,
they saw extensive use in parsing applications. However, we will not
concern ourselves with that here. Rather we simply wish to describe
what DCGs really are and how they are typically used in MERCURY
-programs. We have already seen examples of DCG notation when dealing
-with IO operations taking TT(io__state) parameters.
+programs.
A DCG clause takes the following form
CODE(
@@ -60,54 +59,53 @@ Let's have a quick look at the EM(`Hello, World!') program again.
Here it is in its DCG guise:
CODE(
`main -->
- io__write_string("Hello, World!"),
- io__nl.
+ io.write_string("Hello, World!"),
+ io.nl.
')
and here it is after the DCG transformation (using TT(IO) rather than
TT(X) -- the name doesn't matter):
CODE(
`main(IO0, IO) :-
- io__write_string("Hello, World!", IO0, IO1),
- io__nl(IO1, IO).
+ io.write_string("Hello, World!", IO0, IO1),
+ io.nl(IO1, IO).
')
In case the transformation wasn't obvious, here it is in slow motion:
CODE(
`main -->
- io__write_string("Hello, World!"),
- io__nl.
+ io.write_string("Hello, World!"),
+ io.nl.
DCG(main, IO0, IO) :- DCG((
- io__write_string("Hello, World!"),
- io__nl
+ io.write_string("Hello, World!"),
+ io.nl
), IO0, IO).
main(IO0, IO) :-
- DCG(io__write_string("Hello, World!"), IO0, IO1),
- DCG(io__nl, IO1, IO).
+ DCG(io.write_string("Hello, World!"), IO0, IO1),
+ DCG(io.nl, IO1, IO).
main(IO0, IO) :-
- io__write_string("Hello, World!", IO0, IO1),
- io__nl(IO1, IO).
+ io.write_string("Hello, World!", IO0, IO1),
+ io.nl(IO1, IO).
')
Just to see how the escape clause works, here's a slight variation:
CODE(
`main -->
{ X = "Hello, World!" },
- io__write_string(X),
- io__nl.
+ io.write_string(X),
+ io.nl.
')
which transforms into the following code
CODE(
`main(IO0, IO) :-
{ X = "Hello, World!" },
- io__write_string(X, IO0, IO1),
- io__nl(IO1, IO).
+ io.write_string(X, IO0, IO1),
+ io.nl(IO1, IO).
')
-The key use for DCGs in MERCURY is to hide the TT(io__state) arguments
-which tend to clutter up code. They can also be used in other
-situations where a context argument of some kind is similarly
-threaded. However, it is a good idea only to use DCGs in situations
+The key use for DCGs in MERCURY is to hide a context argument
+that's threaded through a predicate.
+However, it is a good idea only to use DCGs in situations
where such an argument is pervasive, otherwise you will quickly find
that your code becomes an unreadable mess of TT(`{ }'), TT(`-->') and
TT(`:-'). As with all syntactic sugar, one needs to use a little
diff --git a/hello-world.m4 b/hello-world.m4
index 3cec82a..3760a72 100644
--- a/hello-world.m4
+++ b/hello-world.m4
@@ -50,15 +50,11 @@ module therefore become visible in the TT(hello_world) module.
The fourth line looks pretty complicated, but is really quite
straightforward.
CODE(
-`:- pred main(io__state, io__state).
+`:- pred main(io, io).
')
This is a declaration that this module defines a EM(predicate) called
TT(main) that takes two arguments. Each argument is of type
-TT(io__state). The prefix "TT(io__)" says that the type in question is
-defined in the LIB(io) module. In other words, the arguments take the
-type called TT(state) as defined in the LIB(io) module. The token
-"TT(__)" is used to connect module names to names defined within a
-particular module.
+TT(io), defined in the LIB(io) module.
EM(Note:) the predicate defined here will often be referred to using the
shorthand TT(main/2), referring to its name and number of arguments
@@ -89,32 +85,31 @@ intend anyone to import this module).
The last two lines define the predicate itself.
CODE(
-`main -->
- io__write_string("Hello, World!\n").
+`main(!IO) :-
+ io.write_string("Hello, World!\n", !IO).
')
-So, the TT(main) predicate calls the TT(write_string/1) predicate,
+So, the TT(main) predicate calls the TT(write_string/2) predicate,
defined in the LIB(io) library, with the string argument
TT(`"Hello, World!\n"') (the last pair of characters, "TT(\n)", denote
the new-line character, as per C).
But wait! What happened to our promise to the compiler that TT(main)
-took EM(two) arguments -- this one doesn't appear to take EM(any).
+took EM(two) arguments -- this one appears to take only EM(one).
What has happened is that we've defined our predicate using a bit of
-syntactic sugar. Predicates defined using the "TT(-->)" arrow are
-using EM(DCG), or definite clause grammar, notation. Essentially,
+syntactic sugar. Parameters defined using the "TT(!IO)" exclamation are
+using EM(state variable) notation. Essentially,
this has the effect of threading an extra pair of arguments throughout
the code behind the scenes. We could have defined TT(main) as
CODE(
`main(IO_In, IO_Out) :-
- io__write_string("Hello, World!\n", IO_In, IO_Out).
+ io.write_string("Hello, World!\n", IO_In, IO_Out).
')
and the compiler would have been none the wiser. It just happens that
-TT(io__state)s have to be threaded through all calls to the LIB(io)
+TT(io)s have to be threaded through all calls to the LIB(io)
library and that putting them in by hand tends to clutter things up
-unnecessarily. Using "TT(:-)" rather than "TT(-->)" just means that
-extra arguments EM(don't) get threaded through the code.
+unnecessarily.
Now, TT(IO_In) and TT(IO_Out) are said to be EM(logical variables) (you can
detect MERCURY's theory heritage here). Logical variables are
@@ -130,38 +125,28 @@ lower case letter or some punctuation symbol). Henceforth, we'll
simply use the term "EM(variable)" and omit the "EM(logical)".
Since the first argument TT(IO_In) is an input argument, this will be
-bound to some value when TT(main) is called. TT(io__write_string/3)
+bound to some value when TT(main) is called. TT(io.write_string/3)
takes this argument in turn and destroys it in the process of printing
-the string and returns a new, unique TT(io__state). This output gets
+the string and returns a new, unique TT(io). This output gets
bound to TT(IO_Out) which is "returned" by TT(main). You can check that
all this matches up by looking at the declaration for
-TT(io__write_string/3) in the LIB(io) library interface, which declares
+TT(io.write_string/3) in the LIB(io) library interface, which declares
that
CODE(
-`:- pred io__write_string(string, io__state, io__state).
-:- mode io__write_string(in, di, uo) is det.
+`:- pred io.write_string(string, io, io).
+:- mode io.write_string(in, di, uo) is det.
')
Enough of all this. Let's build the thing and fire it up:
CODE(
-`$ mmake hello_world.depend
-mmc --generate-dependencies hello_world
-$ mmake hello_world
-rm -f hello_world.c
-mmc --compile-to-c --grade asm_fast.gc hello_world.m >
-hello_world.err 2>&1
-mgnuc --grade asm_fast.gc -c hello_world.c -o hello_world.o
-c2init --grade asm_fast.gc hello_world.c > hello_world_init.c
-mgnuc --grade asm_fast.gc -c hello_world_init.c -o hello_world_init.o
-ml --grade asm_fast.gc -o hello_world hello_world_init.o hello_world.o
+`$ mmc --make hello_world
+Making Mercury/cs/hello_world.c
+Making Mercury/os/hello_world.o
+Making hello_world
$ ./hello_world
`Hello, World!'
')
-Success! For now, just be aware the MMAKE is a neat wrapper around
-the compiler that hides all sorts of grotty detail from us. The build
-occurs in two stages: first, we tell MMAKE to find all the module
-dependencies for TT(hello_world), such as the LIB(io) library module;
-secondly we tell MMAKE to actually build it. One last thing -- just
+Success! One last thing -- just
as with C, the top-level module of a program has to export a predicate
TT(main/2) exactly like the one in this example which is called when
the program is executed.
diff --git a/name-spaces.m4 b/name-spaces.m4
index fdd6c82..a481c31 100644
--- a/name-spaces.m4
+++ b/name-spaces.m4
@@ -3,7 +3,7 @@ include(defs.m4)
H1(`Modules and Name Spaces')
You may have been wondering why some names imported from other modules
-have been module name-qualified, such as TT(io__state), whereas others
+have been module name-qualified, such as TT(io.state), whereas others
like the TT(list) type have not. Now is the time to reveal the truth
about EM(name spaces).
@@ -47,12 +47,12 @@ CODE(
`:- module baz.
:- interface.
:- import_module foo, bar.
-:- type t1 == foo__t.
-:- type t2 == bar__t.
+:- type t1 == foo.t.
+:- type t2 == bar.t.
...
')
-Module TT(baz) may use TT(foo__t) to refer to the type TT(t) declared
-in the TT(foo) module and TT(bar__t) to refer to the type TT(t)
+Module TT(baz) may use TT(foo.t) to refer to the type TT(t) declared
+in the TT(foo) module and TT(bar.t) to refer to the type TT(t)
declared in the TT(bar) module. The TT(import_module) statements make
all the names that are visible in the interfaces for TT(foo) and
TT(bar) visible in the module TT(baz).
@@ -74,7 +74,7 @@ CODE(
')
Here, the only place that declares the name TT(t) is the module
TT(foo). Therefore its use here is unambiguous and there is no need
-to write out TT(foo__t) in full in the TT(type) declaration. We could
+to write out TT(foo.t) in full in the TT(type) declaration. We could
not use the unqualified type name TT(t) in the preceding example
because it was declared in two distinct imported modules.
@@ -111,11 +111,11 @@ LI the implementation section can see names defined in the interface
section, but not vice versa;
LI public parts of other modules''` name spaces can be imported;
LI an object can always be unambiguously referred to by its
-module-qualified name TT(modulename__objectname);
+module-qualified name TT(modulename.objectname);
LI if the use of a name is unambiguous anyway, one may drop the
-TT(modulename__) prefix -- otherwise the prefix is necessary.
+TT(modulename.) prefix -- otherwise the prefix is necessary.
')
There is one last twist. Rather than writing TT(:- import_module foo)
one can write TT(:- use_module foo). The latter has pretty much the
same effect except that all public names defined in TT(foo) EM(must)
-be fully qualified with a TT(foo__) prefix.
+be fully qualified with a TT(foo.) prefix.
diff --git a/src/facnfib.m b/src/facnfib.m
index d15db63..193b094 100644
--- a/src/facnfib.m
+++ b/src/facnfib.m
@@ -4,18 +4,18 @@
:- import_module io.
-:- pred main(io__state, io__state).
+:- pred main(io, io).
:- mode main(di, uo) is det.
:- implementation.
:- import_module factorial, fibonacci.
-main -->
- io__write_string("factorial(7) = "),
- { factorial(7, X) },
- io__write_int(X),
- io__nl,
- io__write_string("fibonacci(7) = "),
- io__write_int(fibonacci(7)),
- io__nl.
+main(!IO) :-
+ io.write_string("factorial(7) = ", !IO),
+ factorial(7, X),
+ io.write_int(X, !IO),
+ io.nl(!IO),
+ io.write_string("fibonacci(7) = ", !IO),
+ io.write_int(fibonacci(7), !IO),
+ io.nl(!IO).
diff --git a/src/hello_world.m b/src/hello_world.m
index dfcaaa1..2cc5aa0 100644
--- a/src/hello_world.m
+++ b/src/hello_world.m
@@ -4,10 +4,10 @@
:- import_module io.
-:- pred main(io__state, io__state).
+:- pred main(io, io).
:- mode main(di, uo) is det.
:- implementation.
-main -->
- io__write_string("Hello, World!\n").
+main(!IO) :-
+ io.write_string("Hello, World!\n", !IO).
diff --git a/sums-n-things.m4 b/sums-n-things.m4
index 37c4e26..e3e7719 100644
--- a/sums-n-things.m4
+++ b/sums-n-things.m4
@@ -117,12 +117,6 @@ TT(fibonacci/2) is exported from a module called TT(fibonacci):
CODEFILE(src/facnfib.m)
-Look out for the use of TT({ ... }) in TT(main/2) to suppress the
-addition of the extra DCG argument pair in the call to
-TT(factorial/2). The call to TT(fibonacci(7)) doesn't need to be
-escaped in this fashion since the DCG expansion doesn't add the
-extra arguments to nested calls.
-
After compiling and running we get
CODE(
$ ./facnfib
--
1.7.8
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list