Stale image of code in SWI-Prolog?

I’m using: SWI-Prolog version 8.0.2 on Ubuntu Linux 18.04.

I’ve seen the following anomaly more frequently lately, ever since I started separating my code into modules and making greater use of the use_module directive. I will have a certain error in reported by the interpreter when I consult a file that uses other modules. I make a change to fix the error and if re-execute the necessary consult or I do a make operation. In either case the error reported remains the same. If I exit the interpreter and relaunch it, and then execute a new consult operation, the error is now gone. Has anyone seen this issue?

In the meantime, is there some command I can execute that will completely wipe the interpreter’s image of all code files so I don’t have to relaunch the interpreter?

1 Like

Hi, your friend is make/0 from the top level, like this:

?- make.

I am not absolutely clear on when make does not work. I have noticed strange behaviour if there is term/goal expansion. I have a faint recollection that you can run an http server and use make without restarting, unless you change the endpoints, when make is not enough.

2 Likes

You can safely change the endpoints and use make/0. Make/0 should deal with almost all usage scenarios. Surely there are some ways to misuse the module system that make it fail. A good way to cause make/0 to fail is modifying/building the program using assert/1 or in general directives with side effects where calling them multiple times causes problems. We only have “a certain error” and no concrete source to draw conclusions from though …

1 Like

Yes the scenario you note is common, but not unexpected.

I will expand on what you wrote based on my experience so that others may have more information to help them.

When first learning Prolog it is easier to write all of the code in one file.

Then as one progresses, to start using modules, but only existing modules in libraries and use the use_module/1 directive. However if the module is from a library it will most likely be free of bugs/errors so when the primary source file is loaded with consult, the library modules loaded with use_module/1 will not report an error.

Then one begins to create their own modules and loads them using use_module/1. Now upon the first load of the primary file with consult the other modules are loaded and any errors are reported in both the primary file and modules. If a change is made to one of the module files loaded using use_module and then only consult is used, the fixed source code in the module loaded using use_module will not be loaded and the error will still exist. Through trial and error one discovers, as you noted, that if they shut down the top level and restart and then do the consult, the corrected code loaded via use_module is loaded and the error is no longer present.

However because I use SWI-Prolog with Windows I have come to find that using the menu option FileReload modified files will also reload any modules modified and loaded using use_module, even if they are nested a few layers down.

Capture

I didn’t know about make/0 and have never used make/0 but will start to learn to use it now.

The other nice thing about using FileReload modified files is that if you are using the guitracer,

Capture

you don’t have to run nodebug, consult, gtrace to reload a change, e.g.

Capture

the files will be reloaded and the guitracer left ready to accept another query.

What do you mean by endpoints?

FileReload modified files simply calls make/0. Make walks through all loaded files (see source_file_property/2) and compares the time stamps. Then it reloads the modified files and updates imports/exports using a database that tells it which modules loaded the modified module and with which (import) options. It does work best if you only use modules. Except for very quick tests and one-time max 20 line scripts I always start a new file with a module header :slight_smile: If nothing else, it documents what the public API is.

1 Like

For others to expand on Jan’s note.

Yes, defining a module at the head of the code has been my standard for the past year. Also at the end of the source code file I add a unit test section and then once the code is stable, move the tests into a separate test file.

I especially like that using make/0 will automatically run the unit test in the primary file and all the included modules that have unit test in with the source code. When writing really complicated DCGs, the unit test have saved me many times to let me know the change unexpectedly broke something I had not considered.

See: Controlling the test suite

Example of unit tests being run after a make.

1 Like