[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: Null filename ("") is considered to correspond to an existing, reada

From: Drew Adams
Subject: RE: Null filename ("") is considered to correspond to an existing, readable, and writable file?
Date: Tue, 3 Jan 2006 00:24:35 -0800

    No, it does _not_ have to be a relative file name.  It is _allowed_ to
    be.  If you do not want a relative file name, then why do you specify
    one?  Just start your filename with ~ or /.  The _normal_ situation is
    that both are accepted.  It are the exceptions that should be

Fine. That's exactly what I said, BTW: "The doc strings and the manual say
nothing about using a default directory (yes, apparently) or whether the
FILENAME argument must include a directory (it need not, apparently."

What should be documented is that an empty file name to these functions
tests for the existence, readability, and writability of the directory

Here's one of the doc strings (they're all similar):

 "Return t if file FILENAME exists (whether or not you can read it.)"

Substitute "" for FILENAME and re-read the doc: Return t if file "" exists.
Well, file "" does not exist, so the doc string suggests that nil is
returned. At the very least, that's a plausible way to read it. I'd say even
that it is the only reasonable interpretation of what is written.

Coming back to relative file names - what is the status of ""? It's true
that the manual says that any file name that doesn't start with / or ~ is
considered a relative file name. I'm not sure people will naturally think of
an empty file name that way, although, yes, an empty name does not start
with / (or anything else). And, yes, `file-name-absolute-p' returns nil for
"". I realize that that is the design. I'm not wild about that design, but
my real point is that this interpretation of "" might not be obvious to
everyone who uses these functions.

The Elisp manual says this, in fact:

  "An absolute file name starts with a slash or a tilde (`~'), and a
  relative one does not."

Fair enough. A relative name does not start with a / or ~. What _does_ it
start with? Trick question. Does this statement really, clearly say that a
name that does not start with anything at all is a relative name? Maybe, but
not very clearly, not explicitly.

Don't you agree that there is a legitimate reading of this - or at least an
illegitimate but natural reading of this - as saying that a relative file
name _starts with a character_ different from / and ~? Nothing is said about
the null case _explicitly_, so a reader must carefully read that any "name"
that does not start with / or ~ is a relative name - and that includes the
file "name" that is not a name: an empty string of characters. If someone
reads that definition with the (natural) idea that a name is a non-null
string, then he will not have made the right interpretation.

The rule (definition) might be a good one or it might be a bad one. It might
be arbitrary or well-designed. But it is not _obvious_ (meaning the only
reasonable, natural rule). And it is not expressed clearly enough.

Alternative reasonable definitions are conceivable. Here's one: file names
are non-null strings; an absolute file name is one that starts with
character blah; a relative file name is one that starts with any character
other than blah. The empty string by this definition names _no_ file; it is
_not_ a file name, just like many other strings that don't have proper
file-name syntax in some OSs.

My point is that we could be _clearer_ on this:

1) In the general presentation of absolute and relative file names, we
should explicitly point out that "" is considered to be a file name, and
that, because it does not start with / or ~, by the rule given it is a
relative file name.

2) Even if we point that out in the general definition, we should also point
it out in particular cases where there might naturally be confusion. And
this is one such case. When I see a function named `file-exists-p' that
takes a file name, I expect it to look for a file with that name in the
given directory. No file exists with an empty name in that directory (or in
any other), so I would expect it to return nil. Yes, I would need to be
reminded of the rule.

You can say that my expectation is incorrect, and you'll be correct. My
point is that I might not be the only one to interpret the description thus.
Don't you agree that some programmers will, even after reading this doc,
expect that (file-exists-p foo) will only return non-nil if there is in fact
a file named foo, regardless of foo's value? And there is no file named <no
name> (""), and that is a valid value for foo.

This is reference doc, but it's about communication. It's not a formal spec.
If we want to give just a formal definition (e.g. a grammar) and let people
figure it out, then fine, let's do that. The definition as given is not
formal, however, and it is open to interpretation (more than one
interpretation). So, let's help readers by interpreting it a little more
clearly: explicitly point out the null-name case.

The empty name is a legitimate relative file name in Emacs, so the file to
be tested is in fact this name tacked onto the directory name. We should
mention that explicitly, that's all. You can't use an empty file name that
way on the command line or in other common contexts - a file name there is
always non-empty, so not everyone will naturally make the right
interpretation of the empty "name" here.

       And they should explicitly point out the case of "". All the
       more so because their names can easily mislead you into
       thinking that an argument of "" will return a nil result.

    Why do you absolutely want to create files with the empty string as
    name?  It does not work, at least not on POSIX systems.  Does it work
    on MS Windows?

I don't want that. What gave you that idea? My point is precisely that there
is _no_ such file, as you confirm. It doesn't exist. It isn't readable. It
isn't writable. But our test functions say, by their _names_, that a file
named <no name> exists, it is readable, and it is writable. Yes, I know that
that is not what they really mean, but that is what their names say - at
least to some people.

I repeat my original example:

 (while (or (string= "" name) (not (file-readable-p name)))
   (setq name (read-file-name prompt...)))

This is the kind of thing you must write, because `file-readable-p' returns
non-nil for name="". I think it's regrettable - I still haven't heard any
advantage to this design - but so be it. My point is that this needs to be
pointed out.

       Besides, where is the section of the manual that says that,
       unless stated otherwise, all file-name arguments to functions
       are relative, and they are all relative to the

    They are _not_ relative unless the docstring states otherwise.

Precisely. We depend on the doc string to let us know what an argument is. A
file-name argument could take any form - it depends on what the particular
_function_ expects. These particular functions, as you point out, accept
both relative and absolute names. I would expect a reference manual to say
that, and to point out (again) that as a result of the general rule that
says `"" is taken as a relative file name', the file tested is, in fact the

    They are relative unless the file name specifies otherwise, that is, if
    file name starts with / or ~.  "" does not start with / or ~,

Or / or \ or a drive specification (X:/ or X:\) or VMS's ...

    hence it is relative.  Read `(elisp)Relative File Names'.

See above.

       Beyond the doc, wouldn't you expect functions with these
       names to return nil for a "" argument?

    Of course not.  If default-directory is "/mydir/", then "a" is the
    file "/mydir/a", the concatenation of /mydir/" and "a".  Why would
    you expect "" to be anything else but "/mydir/", the concatenation of
    "/mydir/ and "" ?.

See above. You're just parroting the existing design (faithfully). I know
how it works. I'm talking about the expectation that someone might have when
he sees the _name_ `file-exists-p'. You are so familiar with the Emacs
interpretation of "relative file name" that you can't see that someone might
read the function name differently, given that there is nothing in the doc
string to teach him otherwise.  "" is _not_ a relative file name on Unix or
GNU/Linux or Windows or ... It is not so bizarre to think that a function
that tests for the existence of a file named <no name> would return nil, and
there is nothing in the doc string to dissuade such an interpretation.

       Is it so that you can use (file-exists-p "") to test the existence of
       directory `default-directory'?

    Of course.

       What's the advantage of such a "feature"?

    That it makes sense and is consistent.

Baloney. As I said, (file-exists-p default-directory) provides the same
functionality. There are lots of designs that make sense and are consistent.
What's the _advantage_ of this one?

       Why is the design like this?

    Because it is logical and consistent.  And it is POSIX's
    design, not Emacs'.

Hmm. I'm no expert on any of this. I just think the doc could be clearer.

However, googling for POSIX and "relative file name", I do see this, in "The
GNU C Library Reference Manual", section "Input/Output Overview"

  A file name that names a directory may optionally end in a `/'.
  You can specify a file name of `/' to refer to the root directory,
  but the empty string is not a meaningful file name. If you want to
  refer to the current working directory, use a file name of `.' or

The empty string is not only not a relative file name, it is not any file
name at all. The context here is specifically about POSIX (IIUC).

Reading other paragraphs in the same "File Names" section teaches me that 1)
a "file-name component" cannot be empty (multiple successive / are treated
as a single /), and 2) a "relative file name" starts with a file-name
component (a component which doesn't start with a /). To me, that implies 3)
that a relative file name in POSIX cannot be an empty string.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]