[mercury-users] full and empty lists

Ralph Becket rafe at cs.mu.OZ.AU
Mon Jan 14 11:20:15 AEDT 2002


Dave Slutzkin, Sunday, 13 January 2002:
> Hi all.
> 
> :- mode ne_tl_io == ground >> non_empty_list.
> :- mode ne_tl_in == non_empty_list >> non_empty_list.

You can use the built-in parametric modes in/1 and out/1 here to say the
same thing:

:- mode ne_tl_io == out(non_empty_list).
:- mode ne_tl_in == in(non_empty_list).

Why did you pick the name "ne_tl_io"?
                                 ^^
Surely you mean "ne_tl_out"?

> :- func empty_timeline = (timeline::out) is det.
> :- pred full_timeline(timeline::ne_tl_io) is semidet.
> 
> empty_timeline = [].
> full_timeline([_|_]).

The mode for full_timeline/1 is wrong.

If it is intended to construct a partially instantiated list then you
need

:- mode ne_tl_skel_out == out(bound([free | free])).

:- mode full_timeline(ne_tl_skel_out) is det.

However, I'm not sure that the current mode system will be happy with
the use of partially instantiated data (Dave?)


If it is intended to *match* against any non-empty list argument then just

:- mode full_timeline(in) is semidet.

will do.


If it is intended only to be *used* on non-empty inputs then

:- mode full_timeline(ne_tl_in) is det.

but that would be rather pointless since it's guaranteed to succeed for
all valid calls!

> Bascially - I was hoping that empty_timeline and
> full_timeline could be used in a switch statement the
> same way as [] and [_|_].

Okay, we have a summer student, Lars Yencken, who is setting about
adding support for this sort of thing.

The idea is that you could write something like this:

:- func empty_timeline = timeline.

:- func non_empty_timeline = timeline.
:- mode non_empty_timeline = out(bound([free | free])) is det.

:- all [X] promise_exhaustive_and_exclusive
	(	X = empty_timeline
	;	X = non_empty_timeline
	).

And then write code like

foo(empty_timeline) :- ...
foo(non_empty_timeline) :- ...

You couldn't use a pred for non_empty_timeline because the compiler
first translates your code into super-homogeneous normal form as

foo(HeadVar1) :- HeadVar1 = empty_timeline, ...
foo(HeadVar1) :- HeadVar1 = non_empty_timeline, ...

But if non_empty_timeline was a pred then you'd get a type mismatch -
HeadVar1 has type timeline whereas non_empty_timeline would have type
pred(timeline).

> Also, after a call to full_timeline, I was hoping that
> the compiler would realise that I had a non-empty list
> and so I could save this checking in many later
> predicates.  Another snippet:
> 
> :- pred crop_timeline(timeline::in,time::in,
> timeline::out) is det.

It's better style to use funcs when you have modes of this kind.

> crop_timeline(Line,T,NewLine) :-
>     Line = empty_timeline ,
>     NewLine = empty_timeline
> ;
>     full_timeline(Line) ,
>     % we know we have a non-empty list here
>     E = first_event(Line) ,
> .
> .
> .
> 
> Note the use of empty_timeline and full_timeline in
> the switch (are they guards?).  And also that
> first_event can be deterministic, because it takes a
> non-empty list so will always succeed.

If the compiler performed inlining before doing mode analysis, this
would (could be...) be true - but it would be more costly to do things
that way round.

For this example, it's definitely easier to just switch on [] and [_|_].
If you want to avoid checking later that you have a non-empty list then
you could do something like this:

crop_timeline([], _) = [].

crop_timeline(Line, T) = NewLine :-
	Line = [_|_],
	NewLine = crop_ne_timeline(Line, T).


:- func crop_ne_timeline(timeline, time) = timeline.
:- mode crop_ne_timeline(ne_tl_in, in) = out is det.

crop_ne_timeline(NELine, T) = NewLine :-
	...

Now the code for crop_ne_timeline/2 doesn't have to keep checking that
NELine is non-empty.

> Well, it doesn't work.  The compiler still sees this
> code as nondeterministic - I quote:
> 
> event.m:108:   In the return value of call to function
> `event:empty_timeline/0':
> event.m:108:   unification with `Line' can fail.
> event.m:111:   call to
> `full_timeline((event:ne_tl_io))' can fail.
> 
> However, it still works when I replace empty_timeline
> with [] and full_timeline with [_|_] - although
> without the mode benefits of non_empty_list.
> 
> Is there anything I can do about this?  Does anyone
> even understand the problem?  Otherwise I'll have to
> go back to the less modular, less deterministic
> version.

Hope the above helps.

- 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