Defer in Prolog

In Prolog, we don’t have a direct equivalent of the defer keyword. However, we can achieve similar functionality using Prolog’s backtracking mechanism and the call_cleanup/2 predicate. This predicate allows us to specify a goal to be called when the main predicate exits, either successfully or through an exception.

Let’s see how we can implement a similar concept to the original example:

:- use_module(library(system)).

main :-
    setup_call_cleanup(
        create_file('/tmp/defer.txt', Stream),
        write_file(Stream),
        close_file(Stream)
    ).

create_file(Path, Stream) :-
    write('creating'), nl,
    open(Path, write, Stream).

write_file(Stream) :-
    write('writing'), nl,
    write(Stream, 'data'), nl.

close_file(Stream) :-
    write('closing'), nl,
    close(Stream).

In this Prolog version:

  1. We use setup_call_cleanup/3 to mimic the behavior of defer. This predicate takes three arguments:

    • The setup goal (create_file/2)
    • The main goal (write_file/1)
    • The cleanup goal (close_file/1)
  2. The create_file/2 predicate opens a file for writing and returns the stream.

  3. The write_file/1 predicate writes data to the stream.

  4. The close_file/1 predicate closes the stream.

  5. The setup_call_cleanup/3 predicate ensures that the cleanup goal (close_file/1) is called when the main goal (write_file/1) finishes, regardless of whether it succeeds or fails.

To run this program:

$ swipl -q -t main -s defer.pl
creating
writing
closing

This Prolog implementation achieves a similar result to the original example. It ensures that the file is closed after being written, even if an error occurs during the writing process.

Note that Prolog’s approach to file handling and error management is quite different from imperative languages. In Prolog, we typically use predicates that succeed or fail, rather than throwing exceptions. However, if exception handling is needed, Prolog does provide mechanisms for that as well.