# Difference between ls/1 and use\_module/1 when finding a module

**URL:** <https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669>\
**Category:** General\
**Created:** [August 5, 2024, 2:04pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669 "2024-08-05T14:04:52Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [August 5, 2024, 2:04pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/1 "2024-08-05T14:04:52Z")

</div>

After updating to the latest daily build for windows I get an error when I load a module using an explicit path, like “lib/module.pl” but not when I use a user-set file search path like “lib(‘module.pl’)”. At the same time, `ls/1` can find the file with both paths.

Here’s what the whole setup looks like on disk:

```prolog
.
│ config.pl
│ load.pl
│
├───lib
│ target_module.pl
│
└───src
        module_path.pl

```

Here’s what the error looks like. Below, the `module_path.pl` module is trying to load the `lib/target_module.pl` module, taking its path from a predicate in the `config.pl` module. It first calls `ls/1` with the same path:

```prolog
% lib/target_module.pl   
ERROR: c:/module_paths/src/module_path.pl:5:
ERROR: source_sink `lib/'target_module.pl'' does not exist
Warning: c:/module_paths/src/module_path.pl:5:
Warning: Goal (directive) failed: module_path:(config:target_path(_14496),ls(_14496),use_module(_14496))
Welcome to SWI-Prolog (threaded, 64 bits, version 9.3.8-27-g80f956be2)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

```

Note that the first line is output from `ls/1` and it can see the target path.

I think `use_module/1` raises an error because there is no `lib/target_module.pl` path under the same directory as `module_path.pl` - but then why does `ls/1` not raise a similar error?

The example is a little convoluted- there may be an easier way to show the same errors.

Here are the file contents:

```prolog
% load.pl
:-prolog_load_context(directory, Dir)
,asserta(user:file_search_path(project_root, Dir)).

user:file_search_path(src, project_root(src)).
user:file_search_path(lib, project_root(lib)).

:-use_module(src(module_path)).

% config.pl
:-module(config, [target_path/1]).

% Raises error
target_path(lib/'target_module.pl').

% Doesn't raise error.
%target_path(lib('target_module.pl')).

% Gives wearning.
%target_path(lib/target_module).

% src/module_path.pl
:-module(module_path,[]).

:-use_module(project_root(config)).

:-config:target_path(P)
  ,ls(P)
  ,use_module(P).

% lib/target_module.pl
:-module(target_module, [pred/1
                        ]).
pred(1).

```

What is the best way to upload this example? Should I send an archive to [bugs@swi-prolog.org](mailto:bugs@swi-prolog.org) ?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [August 5, 2024, 2:25pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/2 "2024-08-05T14:25:04Z")

</div>

If I read this right, this is not a bug, but a deliberate change. Before, a relative path for use\_module/1 (etc.) was resolved first relative to the file in which it appears and then relative to the process working directory. The “then” part no longer works.

So, you need to load `'../lib/target_module'`. This works in the old system as well and makes sure you can load the code regardless of the process working directory.

Alternatively, setup file search paths early in the load process and use these.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [August 5, 2024, 3:12pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/3 "2024-08-05T15:12:36Z")

</div>

Thanks Jan, to be honest the syntax that raises the error does seem wrong so I thought the (new) error probably wasn’t a bug but the result of a purposeful change.

How about `ls/1` though? Shouldn’t it also have the same behaviour? If I call it from the same file as `use_module/1`, shouldn’t it only look for the path relative to the file?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [August 5, 2024, 3:25pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/4 "2024-08-05T15:25:34Z")

</div>

> [@stassa.p](#):
>
> How about `ls/1` though? Shouldn’t it also have the same behaviour? If I call it from the same file as `use_module/1`, shouldn’t it only look for the path relative to the file?

I guess that is debatable. The impact is in absolute\_file\_name/3. I don’t know by heart what ls/1 uses. If I recall correctly (see docs), absolute\_file\_name/3 interprets relative files as relative to the file for directives.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [August 5, 2024, 4:27pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/5 "2024-08-05T16:27:46Z")

</div>

From what I can see, `ls/1` ultimately calls `name_to_files_/3` (note the underscore) which calls `absolute_file_name/3` but only if the input is a compound of arity 1. It calls `expand_file_name/2` otherwise.

With the path I’m looking for in my example above, ` lib/'target_module.pl'`, which is a compound with arity 2 (//2) this is more or less what happens, from what I can see:

```prolog
% This is the name_to_files_/3 clause that should be reached from my directive; 
% Exists is set to 'true' in the ls/1 call:
name_to_files_(Spec, Files, Exists) :-
    file_name_to_atom(Spec, S1),
    expand_file_name(S1, Files0),
    ( Exists == true,
        Files0 == [S1],
        \+ access_file(S1, exist)
    -> warning('"~w" does not exist', [S1]),
        fail
    ; Files = Files0
    ).

% This is the result, simulated at the top-level:
?- config:target_path(P), shell:file_name_to_atom(P,A), expand_file_name(A,Fs), access_file(A,exist).
P = lib/'target_module.pl',
A = 'lib/target_module.pl',
Fs = ['lib/target_module.pl'].

```

I can’t see what `access_file/2` does (it’s foreign) but I guess it takes the path as relative to the process, unlike `use_module/1`? Otherwise there would be a warning that the file does not exist.

If I call `absolute_file_name/3` on `'lib/target_module.pl'` with the option `access(exist)` which is called if the input is an arity-1 compound and I think is doing something similar to the `access_file/2` check, I do get an error.

```prolog
% name_to_files_/3 clause hit when Spec is an arity-1 compound:
name_to_files_(Spec, Files, _) :-
    compound(Spec),
    compound_name_arity(Spec, _Alias, 1),
    !,
    findall(File,
            ( absolute_file_name(Spec, File,
                                   [ access(exist),
                                     file_type(directory),
                                     file_errors(fail),
                                     solutions(all)
                                   ])
            ; absolute_file_name(Spec, File,
                                   [ access(exist),
                                     file_errors(fail),
                                     solutions(all)
                                   ])
            ),
            Files).

% In module_path.pl:
:-config:target_path(P)
  ,absolute_file_name(P,Abs,[access(exist)])
  ,writeln(Abs).

?- make.
ERROR: c:/module_paths/src/module_path.pl:33:
ERROR: source_sink `lib/'target_module.pl'' does not exist
Warning: c:/module_paths/src/module_path.pl:33:
Warning: Goal (directive) failed: module_path:(config:target_path(_12252),absolute_file_name(_12252,_12332,[access(exist)]),writeln(_12332))
% c:/module_paths/src/module_path compiled into module_path 0.00 sec, 0 clauses
true.

```

So is this consistent behaviour or not? I have no idea. What is the intent of treating compounds of arity 1 in a special way?

(Sorry for the multiple edits- I hope you get to read the whole thing in one go)

Edit: hang on, I think I can answer my own question: the point of checking for an arity-1 compound is to catch terms `Alias(Relative)` like it says in the docs for `absolute_file_name/3`. So really the mistake in my code is to use ‘lib’ as the start of a path rather than an alias. But I still don’t know whether the behaviour of `ls/1` makes sense or not.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [August 5, 2024, 7:01pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/6 "2024-08-05T19:01:29Z")

</div>

> [@stassa.p](#):
>
> So is this consistent behaviour or not?

Hard to say. As is, if you use Alias(File) it uses the logic of absolute\_file\_name/3, which works relative to the source being loaded or, if no source is being loaded, relative to the working directory of the process. All other usage of ls/1 does not use absolute\_file\_name/3 and (thus) uses the working directory of the process as base.

I guess it is what it is 🙂 I wrote ls/1 when I was using the minix os which had, due to lacking memory manager on i386, no sharing fork() and thus a system call would double the memory usage. A set of Prolog shell replacement predicates was a lot faster and did not waste memory 🙂 It is still somewhat useful to have OS agnostic quick look around options.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [August 5, 2024, 9:11pm UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/7 "2024-08-05T21:11:48Z")

</div>

I think that makes sense. `name_to_files_/3` essentially wraps the OS ls functionality and adds the ability to handle the `Alias(Relative)` syntax.

That said, I’m trying to remember where it was that I first learned I could use that syntax and whether there is a primer for it in the docs. I think I just noticed the `use_module(library(something))` directives in various files and documentation examples and cottoned on to it. Which probably means it’s a little bit of an advanced feature not immediately accessible to new entrants. I might be wrong- I’ll have to check the documentation.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [August 6, 2024, 10:30am UTC](https://swi-prolog.discourse.group/t/difference-between-ls-1-and-use-module-1-when-finding-a-module/7669/8 "2024-08-06T10:30:40Z")

</div>

Here’s another little gotcha that I found today. Below, the first bit of syntax, with the path as a quoted atom, raises an error when I try to pass it to `use_module/1`, whereas the second bit of syntax, with the path as a `//2` term does not raise any errors:

```prolog
% Raises error when atomic path is passed to use_module/1
experiment_file('lib/grid_master/data/solvers/grid_solver.pl',grid_solver).

% Raises no error:
experiment_file(lib/grid_master/data/solvers/grid_solver,grid_solver).

```

It might seem strange to pass the path explicitly as a quoted atom, as in the first uncommented line above, but the reason was that I was unsure whether that path would be recognised as a path if I passed it around as a term, rather than an atom.

Also, in general my intuition with SWI-Prolog is that paths are relative to the SWI-Prolog process, rather than relative to any source files. So I load all my source files with a “project load file” that also sets file search paths. I start my project by consulting the project load file. Then I refer to loaded files assuming paths are relative to the project load file. After the recent changes this tactic has caused quite a few errors.

My project load file is quite an informal thing so I guess everyone’s use cases will differ but knowing that every path I may pass around my project is relative to the directory where the project load file is run from gave me a certain sense of security.

Btw, here’s my project load file for Louise:

> **[GitHub - stassa/louise: Polynomial-time Meta-Interpretive Learning](https://github.com/stassa/louise)**
>
> Polynomial-time Meta-Interpretive Learning

I guess such a thing is more common when one is using a full-blown IDE to manage a project, like people do with Visual Studio etc.

Anyway I would maybe argue for making everything relative to the initial process, but I suppose that’s a bit like a debate between which one is best, chocolate or strawberry ice cream.

Strawberry. Yech. 😛
