[mercury-users] Re: Mercury Tutorial / Functions

Ralph Becket rafe at cs.mu.OZ.AU
Wed Apr 23 10:36:42 AEST 2003


Hi Goncalo,

the best place to send this sort of request to is the Mercury Users'
mailing list which is read by a large number of people who can offer
help.

To address your problem, I'll start by saying that using arrays is not a
rookie level Mercury technique.  Arrays aren't complicated, but you
first need to have a fair idea of how the rest of the language works (in
particular, how the mode system treats uniqueness).

An array is a vector of values indexed by number with O(1) lookup and
update operations.  In order for the update operation to be O(1) we have
to use update-in-place (as opposed to making a copy of the original
array with the new value at the given index.)  In order to preserve
referential transparency, we therefore have to "destroy" the old version
of the array in such a way that the program is guaranteed never to
refer to it again.  (This allows us to create the new array by just
doing update-in-place on the old array.)  This is what uniqueness is
used for: only unique values can be "destroyed" in the required way and
therefore be subject to update-in-place.

Back to coding.  Just as you have to ensure that the types match up, you
also have to ensure that the modes match up.  In your example you write

> %:- pred myRead(array(T)).
> %:- pred myRead(T).     
> %:- mode myRead(in, in, out) is nondet.
> %:- mode myRead(in) is nondet.

and you've commented this out because it leads to problems.

There are a number of errors here.

(1) You can only have one pred declaration per predicate, not two.
A declaration

:- pred myRead(array(T)).

says that myRead/1's argument is an array of values of any type (T is a
type variable).  A declaration

:- pred myRead(T).

says that myRead/1's argument is a value of any type.

Neither of these sounds like it's what you really want.

Next, you have two mode declarations.  This is not a problem, but again
there are problems.  The declaration

:- mode myRead(in, in, out) is nondet.

says that myRead/3 (not myRead/1, which would be a *different*
predicate) can be called with its first two arguments as inputs and its
third as an output and there may be any number of possible solutions,
including none at all.

The declaration

:- mode myRead(in) is nondet.

says that myRead/1 can be called with its only argument as an input, but
the result is nondeterministic (i.e. can fail or have any number of
results.)  A predicate can only have multiple solutions if it has an
output argument, so this doesn't make much sense.

You then come to define myRead/1 (or myRead/3) and you have many things
confused.

> %myRead(MyArray) -->
> myRead(MyArray):-   

These are two different ways of starting a clause.  The first, using the
--> arrow, uses DCG notation and says there are two "hidden" arguments
tacked on the end (these are usually used to manage the IO state.)  So
this would refer to myRead/3.

The second is a normal clause and refers to myRead/1.  Something
different.

>         io__read_line_as_string(MaybeString),
>         (
>         if { MaybeString = ok(String) }

The { braces } mean "don't add the extra hidden arguments here" and only
apply to DCG code, not normal code.

>         then
>                io__write_string(String),
> 
>                 {
>                 array__set(MyArray,0,String,MyArrayB),
>                 %       array__set(MyArray,1,2,MyArrayB),

I'm not sure what's going on with this commented out call to
array__set/4.

Here's the code to read in a line of strings to an array.

:- pred read_strings(array(string), array(string), io, io).
:- mode read_strings(array_di,      array_uo,      di, uo) is det.
	%
	% The array_di/array_uo modes stand for "destroyed unique input
	% array" and "unique output array".  The di/uo modes stand for
	% general "destroyed unique input" and "unique output".  The IO
	% state, of type io, is also unique.

read_strings(A0, A, IO0, IO) :-
	read_strings_2(0, array_size(A0), A0, A, IO0, IO).

:- pred read_strings_2(int, int, array(string), array(string), io, io).
:- mode read_strings_2(in,  in,  array_di,      array_uo,      di, uo) is det.

read_strings_2(I, Size, A0, A, IO0, IO) :-
	( if Size =< I then
		A  = A0,
		IO = IO0
	  else
		io__read_line_as_string(Result, IO0, IO1),
		(
			Result = ok(String),
			A1     = array__set(A0, I, String),
			read_strings_2(I + 1, Size, A1, A, IO1, IO)
		;
			Result = eof,
				%
				% There are no more lines to read.
			A      = A0,
			IO     = IO1
		;
			Result = error(_),
				%
				% Something went wrong.
			exception__throw(Result)
		)
	).

And you could call this with, say

	Lines0 = array__init(100, ""),
	read_strings(Lines0, Lines, IOin, IOout)

(Don't use DCGs for IO until you understand how IO works.)

However, it's rare that you really need to muck about with arrays.  In
particular, you have uniqueness and all sorts of resizing issues to deal
with when you don't know the final size ahead of time.  Use lists
instead:

:- pred read_strings(list(string), io, io).
:- mode read_strings(out,          di, uo) is det.

read_strings(Ss, IO0, IO) :-
	read_strings_2([], Ss, IO0, IO).

:- pred read_strings_2(list(string), list(string), io, io).
:- mode read_strings_2(in,           out,          di, uo) is det.

read_strings_2(RevSs, Ss, IO0, IO) :-
	io__read_line_as_string(Result, IO0, IO1),
	(
		Result = ok(S),
		read_strings_2([S | RevSs], Ss, IO1, IO)
	;
		Result = eof,
		Ss     = list__reverse(RevSs)
	;
		Result = error(_),
		exception__throw(Result)
	).

Much easier.

Cheers,
	Ralph
--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list