[mercury-users] range of input

Fergus Henderson fjh at cs.mu.OZ.AU
Tue Nov 9 01:26:50 AEDT 1999


On 08-Nov-1999, Robert Ernst Johann JESCHOFNIK <rejj at cat.cs.mu.OZ.AU> wrote:
> 
> As I was walking home from the train station this evening, I found myself
> thinking that it would be nice if there were a way to tell the compiler
> the range of input a particular predicate will receive for each variable.
> This would allow you to use pattern matching / switches with a
> non-exhaustive set of constructors.

For discriminated union types (a.k.a. algebraic types), you can do
that already, using insts as subtypes.
For example:

	:- type fruit ---> apple ; orange ; lemon ; banana.
	:- inst citrus_fruit ---> orange ; lemon.

	:- func num_pips(fruit) = int.
	:- mode num_pips(in(citrus_fruit)) = out is det.
	num_pips(orange) = 42.
	num_pips(lemon) = 35.

	:- pred is_citrus_fruit(fruit).
	:- mode is_citrus_fruit(ground -> citrus_fruit) is semidet.
	is_citrus_fruit(orange).
	is_citrus_fruit(lemon).

	:- func count_pips(fruit) = int.
	count_pips(Fruit) = NumPips :-
		( is_citrus_fruit(Fruit) ->
			NumPips = num_pips(Fruit)
		;
			NumPips = 0
		).

Here the num_pips/1 function takes as input a subset of the
possible values of the `fruit' type.

But if you want to allow integer ranges, then you really
need something that will do runtime checking.

> That wasn't all too clear, so here is an attempt to show what I mean:
> 
> :- pred  fib(int, int).
> :- mode  fib(in, out) is det.
> :- range fib({1;2;>=3}, all).
> 
> fib(1, 1).
> fib(2, 1).
> fib(X, Y) :-
> 	fib(X1, Y1),
> 	fib(X2, Y2),
> 	X + 1 = X1,
> 	X + 2 = X2,
> 	Y1 + Y2 = Y.

Presumably you should have a call to `X >= 3' or `X > 2' in the last clause.
And I think the `+ 1' and `+ 2' should be `- 1' and `- 2' respectively.
Anyway, that seems like a somewhat obfuscated way of writing `fib'.
I'd suggest using a function and writing it like so

	:- func fib(int) = int.
	:- range fib({1;2;>=3}) = all.
	fib(1) = 1.
	fib(2) = 1.
	fib(X) = Y :-
		X >= 3,
		Y = fib(X - 1) + fib(X - 2).

Here I've kept your `:- range' declaration, and I'm still using
separate clauses rather than an if-then-else, so this is not
legal in Mercury as it stands.

> Given "range" information, this wouldn't have to be semidet or multi. Any
> call out of the promised range should call error/1.
> 
> And yes, I'm sure the syntax for the range could/should be much nicer, but
> I'm just interested in the idea at the moment. Does this seem viable?

Let me play devil's advocate for the moment.
You can achieve the same effect by writing

	fib(X) =
		if X < 0 then error("fib: arg < 0") else
		if X = 1 then 1 else
		if X = 2 then 2 else
		fib(X - 1) + fib(X - 2).

	% Currently the standard library only has a predicate
	% version of error/1.  This function version should go
	% in the standard library too.
	:- func error(string) = _.
	:- mode error(in) = out is erroneous.
	error(Msg) = _ :- error(Msg).

So what would be the advantage in letting you write it using
the alternative syntax that you've suggested?

----------

Perhaps you are mixing several different things here.

  (1)	One issue is the idea of expressing properties such as
	admissability constraints which cannot be checked at compile
	time but which can be checked at runtime.

  (2)	Another issue or two is idea of extending determinism inference to 

	  (2a)	support analysis of calls to predicates like `>' and `>='
		on ints, to detect when things are mutually exclusive
		and when they cover all cases.
	and

	  (2b)	take runtime-checked admissability constraints into account.

Regarding (1):

	I think that extending Mercury to support that kind of thing
	would be a very good idea.

	I discussed this a bit in the "assertions summary" [1] that
	I posted to the mercury-developers mailing list. 
	That post included a proposed a syntax for runtime-checked
	post-conditions, but it did not discuss syntax for
	admissability constraints (i.e. preconditions).  However,
	Peter Schachte has previously proposed [2] on the same list
	the following syntax:

		:- admissable fib(X) => X > 0.
 
	I think we need two different variants, one for admissability
	constraints which are checked at runtime and one for those
	which are not.  So perhaps the syntax could be something like
	this:

		:- admissable fib(X) => X > 0.		% unchecked
		:- check admissable fib(X) => X > 0.	% checked at runtime

	However, it might be better to use a keyword or some other special
	syntax rather than `=>', to avoid mixing object level logic with
	meta-level logic.

Regarding (2):

	With (2a) alone, you would be able to write it as

		fib(1) = 1.
		fib(2) = 2.
		fib(X) = Y :-
			X > 2,
			Y = fib(X - 1) + fib(X - 2)
		fib(X) = Y :-
			X < 0,
			error("fib: arg < 0").

	With (2b), you could omit the fourth clause, instead using an
	admissibility declaration such as

		:- check admissable fib(X) => X > 0.

	(2a) is closely related to the proposal which has been
	championed by Richard O'Keefe of allowing users to declare
	which predicates (or goals) are exhaustive and mutual
	exclusive.  However, user declarations of such properties are
	not really an attractive solution to this particular instance
	of the problem, where you are dealing with integer ranges,
	because (at least with a simple approach) each such case would
	require a separate declaration.  You could given a general
	declaration that `X < Y' and `X > Y' are mutually exclusive,
	but in this case the compiler needs to know that `X < 0' and
	`Y > 2' are mutually exclusive, and since `2' is not `0',
	the general declaration doesn't help for this case.
	To avoid a plethora of declarations, I think determinism
	analysis would need to have some knowledge of integer
	arithmetic.  But then it gets hard to know where to stop;
	should the compiler just know (fixed-precision) `int' arithmetic,
	or would it have to know (arbitrary precision) `integer'
	arithmetic too?  What about `float'?  And so on.

	Personally I don't find the code for fib above to be a
	major improvement in readability over the version that I gave
	in the "devil's advocate" section above.  So I don't find
	the reasons for (2a) or (2b) to be compelling.


References:
[1]	<http://www.cs.mu.oz.au/mercury/mailing-lists/mercury-developers/mercury-developers.9804/0181.html>.
[2]	<http://www.cs.mu.oz.au/mercury/mailing-lists/mercury-developers/mercury-developers.9907/0225.html>.
-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3        |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
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