4.15.5 An Example

Suppose you want a routine that is to prompt for a file name and open the file if it can; otherwise it is to prompt the user for a replacement name. If the user enters an empty name, then it is to fail. Otherwise, it is to keep asking the user for a name until something works, and then it is to return the stream that was opened. There is no need to return the file name that was finally used. We can get it from the stream. Code:

retry_open_output(Stream) :-
    ask_query(filename, format('Type name of file to open\n',[]), -, FileName),
    FileName \== '',
    catch(open(FileName, write, Stream),
          (   Error = error(_,Excp),
          ->  print_message(warning, Excp),
          ;   throw(Error)

file_error(existence_error(open(_,_,_), 1, _, _, _)).
file_error(permission_error(open(_,_,_), _, _, _, _)).

:- multifile 'SU_messages':query_class/5.
'SU_messages':query_class(filename, '> ', line, atom_codes, help_query) :- !.

:- multifile 'SU_messages':query_map/4.
'SU_messages':query_map(atom_codes, Codes, success, Atom) :- !,
        (Codes==end_of_file -> Atom = '' ; atom_codes(Atom, Codes)).

Sample session:

| ?- retry_open_output(S).
Type name of file to open
> nodir/nofile
* Existence error in argument 1 of open/3
* file '/tmp/nodir/nofile' does not exist
* goal:  open('nodir/nofile',write,_701)
Type name of file to open
> newfile
S = '$stream'(3491752)

What this example does not catch is as interesting as what it does. All errors except existence and permission errors are re-thrown, as they represent errors in the program. The example also shows that you generally do not want to catch all exceptions that a particular goal might throw.

Send feedback on this subject.