[mercury-users] hi & a feq queries

Thomas Charles CONWAY conway at cs.mu.OZ.AU
Tue Aug 18 08:57:04 AEST 1998


Donald Syme, you write:
> Hi,
> 
> This is my first message to the list so please forgive me if this 
> isn't quite the right place to send these queries. (Also, is
> there an archive of this list??)

We keep a local archive of the list. I'm not sure if there is one
available by ftp, or whatever.

> I have a few queries.  None of these are holding up my work but
> I thought I'd ask anyway.  [ I'm afraid I'm using 0.7, so these may have 
> been addressed in 0.7.3.  I couldn't get 0.7.3 to compile out of the
> box on my linux system, so I went with the stable 0.7... ]

If you want something more recent you could try the release of the day -
quite a lot has changed since 0.7.

>   - I'm surprised Mercury doesn't seem to support data values that
>     are conditionals, e.g. "if <goal> then 1 else 2".  I've had to
>     create a function and apply itt o a dummy argument to get this effect,
>     e.g.  apply((func(X) = Y :- if <goal> then Y = 1 else Y = 2), dummy)
> 
>     Is there an easier way of doing this?  For starters, can I say 
>     something like
> 
>          (func = Y :- if <goal> then Y = 1 else Y = 2)

I just successfully compiled the following piece of code:

	:- func f(int) = string.

	f(N) = (if N mod 2 = 0 then "foo\n" else "bar\n").

The following also works:
	main -->
		...
		io__write_string((if 23 mod 2 = 0 then "foo\n" else "bar\n")),
		...

>   - The definition of a "switch" wrt. mode analysis seems a little strict.
>     For example, the natural translation of pattern matching from 
>     functional languages produces switches like the following:
> 
>         Functional                           Mercury
>       match x with                         (x = [], ... 
>          [] -> ...                        ; x = [h], ...
>        | [h] -> ...                       ; x = [h1,h2|t], ...)
>        | [h1,h2 | t] -> ...
> 
>     The existence of two patterns headed by "cons" ("." in Mercury) 
>     means the mode analysis doesn't believe this is a switch.
>     Shouldn't Mercury look beyond the first head operator and
>     detect the structural difference in the terms further down?

It does. If the compiler was complaining, it probably means that your
switch patterns either missed some cases or were not mutually exclusive.

The following predicate compiles just fine:
	:- pred p(list(int), int).
	:- mode p(in, out) is det.

	p([], -1).
	p([A], A).
	p([_,B|_], B).

However, a common problem is to deal with a singleton list incorrectly:

	:- pred last(list(T), T).
	:- mode last(in, out) is det.

	last([], _) :- error("empty list!").
	last([X], X).
	last([X|Xs], Z) :-
		Xs = [_|_],	% <--- Note this
		last(Xs, Z).

The noted unification is necessary to make the second and third
clauses mutually exclusive, but is easy to miss.

>   - Functional programming switches with default patterns seem
>     difficult to translate into Mercury, e.g.
> 
>         Functional                           Mercury
>       match x with                         switch(x = [] -> ... 
>          [] -> ...                              ; ??? -> ...)
>        | _ -> ...                             
> 
>     I can, of course, manually put in "x = [h|t]" but
>     to do this automatically requires a lot of work on my part: I
>     must unwind the type-structure of "x" and work out the missing 
>     constructors.  I could put in "x \= []" but then Mercury doesn't
>     recognise the construct as a switch (actually I haven't tried this - I'm
>     just basing things off the reference manual).
> 
>     I can see it would be fair to say "well, that's just your problem
>     for trying to translate that sort of thing", but it would seem
>     realistic to consider a general functional-language-style pattern
>     matching construct in Mercury, in both the predicate and
>     expression languages.

The alternative clauses in a functional language are not like disjunction -
they are more like if then else: "if this pattern matches then do *this*
else if this pattern matches do *this* else ..." not "do *this* or do *this*
or ....".

There are two approaches you can use:

Use an if then else (or a chained if then else):
	( X = ... ->
		...
	; X = ... ->
		...
	; % etc
		...
	).

Use an partial switch in the condition of an if-then-else:
	(
		(
			X = [],
			...
		;
			X = [A],
			...
		;
			X = [A, B],
			...
		)
	->
		% rename the output variables -
		% see the language reference manual for more
		% on why.
	;
		% the default case
	).

The second of these techniques is better than the first if there
are many cases to cover because the compiler generates better code
for switches than chains of if-then-elses in the case where the
conditions are all mutually exclusive (except for the default case).

This technique doesn't work properly if the actions need to do I/O, eg:
	:- pred p(list(int), io__state, io__state).
	:- mode p(in, di, uo) is det.

	p(List) -->
		(
			(
				{ List = [] },
				write_string("empty\n")
			;
				{ List = [_] },
				write_string("singleton\n")
			;
				{ List = [_, _] },
				write_string("doubleton\n")
			)
		->
			[]
		;
			write_string("many\n")
		).

The compiler rejects this procedure because it isn't smart enough to
figure out that once the unification of List with the pattern has
succeeded, the condition can no longer fail, so the destructive updates
to the io__state will not be retracted. Here are two ways of fixing 
this code:
	p(List) -->
		(
			(
				{ List = [] },
				{ Str = "empty\n" }
			;
				{ List = [_] },
				{ Str = "signleton\n" }
			;
				{ List = [_, _] },
				{ Str = "doubleton\n" }
			)
		->
			write_string(Str)
		;
			write_string("many\n")
		).
Or more generally:

	p(List) -->
		{ classify_list(List, Class) },
		(
			{ Class = empty },
			write_string("empty\n")
		;
			{ Class = singleton },
			write_string("empty\n")
		;
			{ Class = doubleton },
			write_string("doubleton\n")
		;
			{ Class = many },
			write_string("many\n")
		).

	:- type list_class
		--->	empty
		;	singleton
		;	doubleton
		;	many
		.

	:- pred classify_list(list(T), list_class).
	:- mode classify_list(in, out) is det.

	classify_list(List, Class) :-
		(
			(
				List = [],
				Class0 = empty
			;
				List = [_],
				Class0 = singleton
			;
				List = [_, _],
				Class0 = doubleton
			)
		->
			Class = Class0
		;
			Class = many
		).

This seems like a lot of machinery for something quite simple, but
this is quite general - I've used this technique for parsing lines
of input, converting terms, and other applications.

> 
>   - Mercury rejects the following, which is the natural translation of
>     the axiom
>             "both p x <=> (match p with (y,z) -> (x = y or x = z))"
> 
>     :- module junk.
>     :- import_module std_util.
>     :- pred both(pair(X,X) :: in, X :: out) is multi.
>     both(P,X) :-  call((pred(V1 :: in) is multi :- V1 = (Y-Z), (X = Y; X = Z)),
>                        P).
> 
>     on the grounds that "X" is being bound "under a negation".
>     However it accepts the beta-equivalent
> 

Actually this is a case of the error message not being all it could -
the bug is that you're binding a non-local variable inside the pred
expression. The correct form is:
	both(P,X) :-
		call((pred(V1::in, X1::out) is multi :-
			V1 = Y-Z,
			(
				X1 = Y
			;
				X1 = Z
			)
		), P, X).

Thomas
-- 
Thomas Conway <conway at cs.mu.oz.au>
Nail here [] for new monitor.  )O+



More information about the users mailing list