[m-rev.] for review: library changes for `mmc --make' on Windows

Fergus Henderson fjh at cs.mu.OZ.AU
Tue Jul 15 10:22:07 AEST 2003


On 15-Jul-2003, Simon Taylor <stayl at cs.mu.OZ.AU> wrote:
> On 18-Jun-2003, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> > On 15-Jun-2003, Simon Taylor <stayl at cs.mu.OZ.AU> wrote:
> > > +	% Make the given directory, and all parent directories.
> > > +	% This will also succeed if the directory already exists
> > > +	% and is readable and writable by the current user.
> > > +:- pred dir__make_directory(string, io__res, io__state, io__state).
> > > +:- mode dir__make_directory(in, out, di, uo) is det.
> > 
> > This functionality is a bit too high-level.  While I agree that this
> > functionality is useful, if we're going to add this, then I think
> > we should also add a procedure to just make a single directory,
> > without attempting to make any of the parent directories.
> > 
> > [For the .NET CLI, I think you can create a directory without creating
> > all the subdirectories using "(new System.IO.DirectoryInfo(path)).Create();".]
> 
> When is this useful?

Whenever your software is designed so that some other part of the system
is responsible for creating the parent directories; in such cases,
non-existence of the parent directory could indicate a serious
installation problem that ought to be brought to the attention
of the user.

Also, the simpler functionality will be more efficient on many systems;
this may be important when using network file systems, especially if you
are using mkdir for locking purposes.

> > > +:- pragma foreign_proc("C#",
> > > +	dir__split_name_dotnet(FileName::in, DirName::out, BaseName::out),
> > > +	[may_call_mercury, promise_pure, thread_safe],
> > > +"
> > > +	try {
> > > +		DirName = System.IO.Path.GetDirectoryName(FileName);
> > > +		if (DirName == null) {
> > > +			SUCCESS_INDICATOR = false;
> > > +		} else if (DirName == System.String.Empty) {
> > > +			SUCCESS_INDICATOR = true;
> > > +			DirName = mercury.dir.mercury_code.ML_this_directory();
> > > +			BaseName = FileName;
> > > +		} else {
> > > +			BaseName = System.IO.Path.GetFileName(FileName);
> > > +			SUCCESS_INDICATOR = (BaseName != null);
> > > +		}
> > > +	} catch (System.Exception e) {
> > > +		SUCCESS_INDICATOR = false;
> > > +	}
> > > +").
> > 
> > Is this really pure?
> > 
> > For example, 
> 
> ?

What I meant to say was, does it examine the file system?
Or does it depend on the system's notion of current directory or
current drive, which might be changed during the execution of
the program?

> > > +:- pred is_root_directory(list(char)::in) is semidet.
> > > +is_root_directory(FileName) :-
> > 
> > It would help to document what this is supposed to do for corner cases
> > such as "C:".
> 
> C: is a relative path, so that's clearly not a root directory.

Well, "\foo\bar", which you said above is an absolute directory, is also
a relative path (it is relative to the current drive).  So this business
about root directories and absolute directories is not very clear ;-)

> > > +:- type io__access_type
> > > +	--->	read
> > > +	;	write
> > > +	;	execute
> > > +	.
> > > +
> > > +:- pred io__check_file_accessibility(string, list(access_type),
> > > +		io__res, io__state, io__state).
> > 
> > Shouldn't that be a set(access_type)?
> 
> It could be, but it really doesn't matter.

In that case, the documentation should make it clear that the order
and multiplicity of elements in the list is not important.

> > Why are there three different .exp* files for this test case?
> > Two I can understand, one for Windows-style paths and one for
> > Unix-style paths, but why the third?
> > Could you post a diff of the two different Windows-style versions?
> 
> The difference is only in trailing directory separators on directories --
> the .NET CLI strips them, my code doesn't. Given that in some circumstances
> trailing separators are significant (C: vs C:\), I think it's better not
> to try to strip them.

IMHO the Mercury standard library routines for manipulating paths
should not behave differently on the same (Windows) platform depending
on whether you're compiling to .NET or not.  That would be a recipe
for portability problems.  I think it would be better to pick one
meaning and stick with it, even if that requires more implementation effort.

> library/exception.m:
...
> 	Add predicates try_det, try_io_det and try_store_det,
> 	which only have one mode so they are more convenient
> 	to pass to promise_only solution.

That part of the log message is no longer valid.

> diff -u library/exception.m library/exception.m
> --- library/exception.m
> +++ library/exception.m
...
> +finally_2(P, Cleanup, {PRes, CleanupRes}, !IO) :-
> +	try_io(P, ExcpResult, !IO),
> +	(
> +		ExcpResult = succeeded(PRes),
> +		Cleanup(CleanupRes, !IO)
> +	;
> +		ExcpResult = exception(_),
> +		Cleanup(_, !IO),
> +		% The io__state resulting from Cleanup can't
> +		% possibly be used, so we have to trick the
> +		% compiler into not removing the call.
> +		( use(!.IO) ->
> +			rethrow(ExcpResult)		
> +		;
> +			error("exception.finally_2")
> +		)
> +	).
> +
> +:- pred use(T).
> +:- mode use(in) is semidet.
> +
> +:- pragma foreign_proc("C",
> +	use(_T::in),
> +	[will_not_call_mercury, promise_pure, thread_safe],
> +	"SUCCESS_INDICATOR = MR_TRUE;").
> +:- pragma foreign_proc("C#",
> +	use(_T::in),
> +	[will_not_call_mercury, promise_pure, thread_safe],
> +	"SUCCESS_INDICATOR = true;").
> +:- pragma foreign_proc("Java",
> +	use(_T::in),
> +	[will_not_call_mercury, promise_pure, thread_safe],
> +	"SUCCESS_INDICATOR = true;").

Semidet Java foreign code is currently not supported.  So the Java
foreign_proc here should be commented out or deleted.

But use/1 could be implemented in ordinary Mercury code as

	:- pragma no_inline(use/1).
	use(_).

Still, regardless of whether use/1 is defined by Mercury clauses
or as a foreign proc, calling use/1 in this way is not sufficient to
guarantee that future Mercury compilers will call Cleanup.  For example,
if the call to finally happens to be wrapped up inside some code which
catches exceptions but does not examine which exception was caught,
a clever compiler might reason that it doesn't make any difference
whether rethrow/1 or error/1 is called, and that therefore there is
no need to call use/1.  Admittedly it will be a long time before the
Mercury compiler is that clever, but still :)
Declaring use/1 "impure" would be a bit safer.

Another possible solution which might be a little more elegant would
be to add a dummy argument (polymorphically typed, with mode di) to
throw_impl/1 and builtin_throw/1.

[TBC...]

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list