open-file-stream(..., if-does-not-exist: #f)
The if-does-not-exist: keyword argument to open-file-stream and make(<file-stream>) is documented to accept #f as a possible value, meaning "no action".
-
What "no action" means needs to be documented better, but I take it to mean that for
direction: #"input"no error should be signaled and#fshould be returned, similar to in Common Lisp. Fordirection: #"output"I don't think "no action" makes much sense. -
The unix and win32
accessor-openmethods document in comments that#"create"and#"signal"are the only two valid values. (Even though they don't seem to useif-does-not-existat all!) -
direction: #"input", if-does-not-exist: #fsignals an error when the file doesn't exist.
Maybe direction: #"output", if-does-not-exist: #f is unlikely to be used (only write to the file if it already exists?) but presumably it should do the same as for #"input": just return #f.
I'm just going to throw this in here, since it's somewhat related:
Check what happens for if-does-not-exist: #"error" and other possible misspellings of the symbols #"create" and #"signal". Can we use constants instead to get yummy type safety and compiler warnings? $create and $error would be my choice.
Pinging @cgay ... why haven't you fixed this yet?
The main relevant doc is here: https://opendylan.org/library-reference/system/file-system.html#options-when-creating-file-streams
open-file-stream returns a <stream> or signals an error. This seems right and good. Returning #f the way Common Lisp's open function returns nil just adds complexity for callers.
In Common Lisp, with-open-file binds the stream variable to nil and executes the body, expecting the body code to check for nil when :if-does-not-exist nil is used.
In Dylan we make the stream by calling make(<file-stream>, ...) which cannot return #f, so we need to catch <file-does-not-exist> and do something with it.
(1) We could do what Common Lisp does:
with-open-file (stream = "/no/such/file", if-does-not-exist: #f)
if (stream)
read-to-end(stream)
else
"whatever"
end
end
The above doesn't work currently if you look at the with-open-file code, because it binds the stream to a variable of type <file-stream>.
(2) We could instead skip executing the body and make this be the Dylan idiom:
with-open-file (stream = "/no/such/file", if-does-not-exist: #f)
read-to-end(stream)
end | "whatever"
(3) or we could disallow if-does-not-exist: #f altogether, meaning people would have to write this instead:
block ()
with-open-file (stream = "/no/such/file")
read-to-end(stream)
end
exception (<file-does-not-exist-error>)
"whatever"
end
I personally like the middle option as it's concise and it's what I assumed was meant to happen from the start so at least to me it's the intuitive option.
(1) sort of makes sense in CL because nil is a synonym for stdin/stdout but less so in Dylan.
(2) has the problem that it changes the semantics if the body contains side-effecting code, so we'll forget that idea.
(3) is the least ambiguous and seems to match Dylan style best.
I'll add (4) use something less ambiguous than #f, like #"skip-body".