more than one interface per module

38 views
Skip to first unread message

Dan

unread,
Jan 25, 2018, 5:13:29 AM1/25/18
to SWI-Prolog
Hi, 

I'd like to expose more than one (type) of interface for a module, and thereby more selectively allow importing predicates. 

For example, for testing, i want to expose lower level, "internal" predicates, and for regular use, only higher level ones?

I guess, one simple approach is to split each module into two -- low-level predicates and higher-level predicates, and then expose both to the testing and only one to the client. 

Dan

Boris Vassilev

unread,
Jan 25, 2018, 5:17:26 AM1/25/18
to SWI-Prolog
You should be able to pick any of the "internal" predicates just by qualifying them fully, like

:- module(foo, [foo_a/1]).

foo_a(_).

foo_b(_).

Then:

?- use_module(foo).
?- foo_a(_). % this works
?- foo_b(_). % this does not
?- foo:foo_b(_). % this works

Does that help?


Save our in-boxes! http://emailcharter.org

--
You received this message because you are subscribed to the Google Groups "SWI-Prolog" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swi-prolog+unsubscribe@googlegroups.com.
Visit this group at https://groups.google.com/group/swi-prolog.
For more options, visit https://groups.google.com/d/optout.

Jan Wielemaker

unread,
Jan 25, 2018, 5:38:28 AM1/25/18
to Boris Vassilev, SWI-Prolog
On 25/01/18 11:16, Boris Vassilev wrote:
> You should be able to pick any of the "internal" predicates just by
> qualifying them fully, like
>
> :- module(foo, [foo_a/1]).
>
> foo_a(_).
>
> foo_b(_).
>
> Then:
>
> ?- use_module(foo).
> ?- foo_a(_). % this works
> ?- foo_b(_). % this does not
> ?- foo:foo_b(_). % this works
>
> Does that help?

There are people who think this is disgusting. I'm one of them :)
The big problem is that this breaks one of the most useful properties
of a module: you can hack away as long as you do not touch the
exported API. You can get some of this back using the public
declaration:

:- public
foo_b/1.

It doesn't really do anything but documents the fact that foo_b/1
can be called from outside. If you use the built-in editor it
colours the heads of the public predicates green, so you know you
should leave those alone while refactoring the module.

If I have a complex project I typically split it into modules in
a subdirectory and then add one module in the main dir that loads
the others (possibly indirectly) and exposes the high level
interface. A user normally includes just the high level
interface, but may additionally load one or more of the low level
ones or even just the low level without the high level one to
use part of te functionality.

Cheers --- Jan

>
>
> Save our in-boxes! http://emailcharter.org
>
> On Thu, Jan 25, 2018 at 12:13 PM, Dan <gros...@gmail.com
> <mailto:gros...@gmail.com>> wrote:
>
> Hi, 
>
> I'd like to expose more than one (type) of interface for a module,
> and thereby more selectively allow importing predicates. 
>
> For example, for testing, i want to expose lower level, "internal"
> predicates, and for regular use, only higher level ones?
>
> I guess, one simple approach is to split each module into two --
> low-level predicates and higher-level predicates, and then expose
> both to the testing and only one to the client. 
>
> Dan
>
> --
> You received this message because you are subscribed to the Google
> Groups "SWI-Prolog" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to swi-prolog+...@googlegroups.com
> <mailto:swi-prolog+...@googlegroups.com>.
> <https://groups.google.com/group/swi-prolog>.
> For more options, visit https://groups.google.com/d/optout
> <https://groups.google.com/d/optout>.
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "SWI-Prolog" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to swi-prolog+...@googlegroups.com
> <mailto:swi-prolog+...@googlegroups.com>.

Dan

unread,
Jan 25, 2018, 6:19:45 AM1/25/18
to SWI-Prolog
Hi all, 

Thank you. 

Perhaps for the testing code, exposing the internal predicate via explicit qualification is good enough. 

While "real" client code, is not "allowed" to do this ...

Although, it does leave a "bad taste" since the purpose of testing is to ensure that the code works as expected, which includes, testing it in the manner its packaged as well. 

Dan

Boris Vassilev

unread,
Jan 25, 2018, 6:28:11 AM1/25/18
to SWI-Prolog
PS: are you using the PlUnit package for testing? I guess this is one way to test the "internal" predicates.

Save our in-boxes! http://emailcharter.org

--
You received this message because you are subscribed to the Google Groups "SWI-Prolog" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swi-prolog+unsubscribe@googlegroups.com.

Dan

unread,
Jan 25, 2018, 6:30:31 AM1/25/18
to SWI-Prolog
yes. i do use it. 

It's very helpful. 

Dan


On Thursday, 25 January 2018 13:28:11 UTC+2, boris.vassilev wrote:
PS: are you using the PlUnit package for testing? I guess this is one way to test the "internal" predicates.

Save our in-boxes! http://emailcharter.org

On Thu, Jan 25, 2018 at 1:19 PM, Dan <gros...@gmail.com> wrote:
Hi all, 

Thank you. 

Perhaps for the testing code, exposing the internal predicate via explicit qualification is good enough. 

While "real" client code, is not "allowed" to do this ...

Although, it does leave a "bad taste" since the purpose of testing is to ensure that the code works as expected, which includes, testing it in the manner its packaged as well. 

Dan



On Thursday, 25 January 2018 12:13:29 UTC+2, Dan wrote:
Hi, 

I'd like to expose more than one (type) of interface for a module, and thereby more selectively allow importing predicates. 

For example, for testing, i want to expose lower level, "internal" predicates, and for regular use, only higher level ones?

I guess, one simple approach is to split each module into two -- low-level predicates and higher-level predicates, and then expose both to the testing and only one to the client. 

Dan

--
You received this message because you are subscribed to the Google Groups "SWI-Prolog" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swi-prolog+...@googlegroups.com.

Boris Vassilev

unread,
Jan 25, 2018, 6:42:29 AM1/25/18
to SWI-Prolog
Hello Jan,

I wouldn't go as far as calling it "disgusting" ;-) I really like the freedom it gives you. The strict separation of "interface" and "implementation" is a factor when you actually don't want to distribute the implementation as source, right?

Example: you have already unpacked the head and the tail of a list; the "public" predicate takes the whole list, only to unpack it again before passing it to the helper predicate.

Is it bad to call the "private" helper predicate directly, in such cases?

Save our in-boxes! http://emailcharter.org

>
>
> --
> You received this message because you are subscribed to the Google
> Groups "SWI-Prolog" group.
> To unsubscribe from this group and stop receiving emails from it, send

Jan Wielemaker

unread,
Jan 25, 2018, 6:52:12 AM1/25/18
to Boris Vassilev, SWI-Prolog
On 25/01/18 12:41, Boris Vassilev wrote:
> Hello Jan,
>
> I wouldn't go as far as calling it "disgusting" ;-) I really like the
> freedom it gives you. The strict separation of "interface" and
> "implementation" is a factor when you actually don't want to distribute
> the implementation as source, right?

As source grows and more people are working on it, the promises provided
by an interface become really valuable. Especially during development
it is nice that you can easily get to the internals though. Many
languages and developers what isolation to be enforced. I'm happy with
a more liberal approach.

SWI-Prolog is really open and you can also call the internals of
compiled (.qlf) modules :) It is only a little harder to find what
the internals are.

> Example: you have already unpacked the head and the tail of a list; the
> "public" predicate takes the whole list, only to unpack it again before
> passing it to the helper predicate.
>
> Is it bad to call the "private" helper predicate directly, in such cases?

I would not do that. The performance loss should be neglectable in this
case. If it is more serious your interface is not properly designed.

Cheers --- Jan

>
> Save our in-boxes! http://emailcharter.org
>
> >     send an email to swi-prolog+...@googlegroups.com
> <mailto:swi-prolog%2Bunsu...@googlegroups.com>
> >     <mailto:swi-prolog+...@googlegroups.com
> <mailto:swi-prolog%2Bunsu...@googlegroups.com>>.
> >     Visit this group at https://groups.google.com/group/swi-prolog
> <https://groups.google.com/group/swi-prolog>
> >     <https://groups.google.com/group/swi-prolog
> <https://groups.google.com/group/swi-prolog>>.
> >     For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>
> >     <https://groups.google.com/d/optout
> <https://groups.google.com/d/optout>>.
> >
> >
> > --
> > You received this message because you are subscribed to the Google
> > Groups "SWI-Prolog" group.
> > To unsubscribe from this group and stop receiving emails from it, send
> > an email to swi-prolog+...@googlegroups.com
> <mailto:swi-prolog%2Bunsu...@googlegroups.com>
> > <mailto:swi-prolog+...@googlegroups.com
> <mailto:swi-prolog%2Bunsu...@googlegroups.com>>.
> > Visit this group at https://groups.google.com/group/swi-prolog
> <https://groups.google.com/group/swi-prolog>.
> > For more options, visit https://groups.google.com/d/optout
> <https://groups.google.com/d/optout>.
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "SWI-Prolog" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to swi-prolog+...@googlegroups.com
> <mailto:swi-prolog+...@googlegroups.com>.

Dan

unread,
Jan 25, 2018, 9:47:36 AM1/25/18
to SWI-Prolog
Hi Boris, 

Can you indicate a bit more how one uses compiled modules. 

I am looking for a way to protect the source -- could precompiled modules be the way to go? Would this offer some (reasonable) protection of the source code, when for example, placing the compiled module onto a publicly accessible website or on the cloud?

thank you,

Dan


On Thursday, 25 January 2018 12:13:29 UTC+2, Dan wrote:

Jan Wielemaker

unread,
Jan 25, 2018, 3:54:56 PM1/25/18
to Dan, SWI-Prolog
On 25/01/18 15:47, Dan wrote:
> Hi Boris,
>
> Can you indicate a bit more how one uses compiled modules.
>
> I am looking for a way to protect the source -- could precompiled
> modules be the way to go? Would this offer some (reasonable) protection
> of the source code, when for example, placing the compiled module onto a
> publicly accessible website or on the cloud?

A .qlf file provides very limited security. Anyone can load it and run
?- listing. to get the code. Real variable names, comments and the order
of predicates has gone, but that is all. If you want some level of
protection you must use a saved state and somehow ensure the user can
not really get at the saved state itself, i.e., by encrypting it and
have an virtual machine that can ensure it has not be tempored with and
can load the encrypted state. In that case you can disable decompilation
of static code and if you can deny the user the possibility to revert
that you're fairly safe. The latter means you somehow need to disable
priviledged access to the OS.

For short, this is hard and only realistic if you can seal down access
to the hardware and its firmware. More feasible is a source-to-source
compiler similar to JavaScript code minification that would not only
loose variable names, but all identifiers. Reading Prolog code where
predicates are called p1, p2, ..., modules m1, m2, ... and variables
A,B,C, ... is really hard. Writing such a conversion has been discussed
several time, but so far never found the funding and interest to really
do it.

Cheers --- Jan

Peter Ludemann

unread,
Jan 25, 2018, 10:55:40 PM1/25/18
to Jan Wielemaker, Dan, SWI-Prolog
Python seems to do fine with a convention that names staring with "_" are internal and other names are external (with a further convention that lists the names you get if you do an "import *"). (The "pylint" tool will complain if you access an internal name from another module.)

The idea is that if you use an internal name, you have no right to complain if it changes or disappears. Similarly, for Prolog, if you use a predicate that's not listed as exported (by using the module:predicate form), you have no right to complain. Why would you need anything stronger?

- p

Inline images 1


--
You received this message because you are subscribed to the Google Groups "SWI-Prolog" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swi-prolog+unsubscribe@googlegroups.com.

Paulo Moura

unread,
Jan 26, 2018, 6:22:40 AM1/26/18
to SWI-Prolog
Hi,

> On 26 Jan 2018, at 03:54, Peter Ludemann <peter.l...@gmail.com> wrote:
>
> Python seems to do fine with a convention that names staring with "_" are internal and other names are external (with a further convention that lists the names you get if you do an "import *"). (The "pylint" tool will complain if you access an internal name from another module.)

On the other hand, Prolog compilers (in general) are too permissive and often let the user get away with practices that are at best questionable. Calling a non-exported predicate using explicit qualification is one of them.

> The idea is that if you use an internal name, you have no right to complain if it changes or disappears. Similarly, for Prolog, if you use a predicate that's not listed as exported (by using the module:predicate form), you have no right to complain. Why would you need anything stronger?

That's a slippery slop. It may work for solo code warriors. Or for applications where a single individual can be aware all the details. I.e. when programming in the small. But in organizations with several developers, where new ones are hired and older ones move out but complex applications must be kept running, enforcing sane practices, including not breaking encapsulation boundaries, is a most. Of course, the availability and quality of tools make a difference.

Cheers,
Paulo

Peter Ludemann

unread,
Jan 26, 2018, 9:08:11 AM1/26/18
to Paulo Moura, SWI-Prolog
On 26 January 2018 at 03:22, Paulo Moura <pjlm...@gmail.com> wrote:
Hi,

> On 26 Jan 2018, at 03:54, Peter Ludemann <peter.l...@gmail.com> wrote:
>
> Python seems to do fine with a convention that names staring with "_" are internal and other names are external (with a further convention that lists the names you get if you do an "import *"). (The "pylint" tool will complain if you access an internal name from another module.)

On the other hand, Prolog compilers (in general) are too permissive and often let the user get away with practices that are at best questionable. Calling a non-exported predicate using explicit qualification is one of them.

> The idea is that if you use an internal name, you have no right to complain if it changes or disappears. Similarly, for Prolog, if you use a predicate that's not listed as exported (by using the module:predicate form), you have no right to complain. Why would you need anything stronger?

That's a slippery slop. It may work for solo code warriors. Or for applications where a single individual can be aware all the details. I.e. when programming in the small. But in organizations with several developers, where new ones are hired and older ones move out but complex applications must be kept running, enforcing sane practices, including not breaking encapsulation boundaries, is a most. Of course, the availability and quality of tools make a difference.

​It works at Google with 10s of thousands of engineers. Maybe this is because Google has a strong code-review culture, plus an enforced style guide.
There's no need to be "aware of all the details" -- the conventions of only using the public APIs avoids that problem. Someone who wants to break the convention does need to be aware of the details, and is knowingly inviting trouble.
One exception to the convention is for unit tests... but that's OK because the unit test can be commented that it breaks the convention and is solely for validating some internal-only code.
- p​

 

Cheers,
Paulo

Paulo Moura

unread,
Jan 26, 2018, 9:57:12 AM1/26/18
to SWI-Prolog
Hi Peter,

> On 26 Jan 2018, at 14:07, Peter Ludemann <peter.l...@gmail.com> wrote:
>
>
>
> On 26 January 2018 at 03:22, Paulo Moura <pjlm...@gmail.com> wrote:
> Hi,
>
>> > On 26 Jan 2018, at 03:54, Peter Ludemann <peter.l...@gmail.com> wrote:
>> >
>> > Python seems to do fine with a convention that names staring with "_" are internal and other names are external (with a further convention that lists the names you get if you do an "import *"). (The "pylint" tool will complain if you access an internal name from another module.)
>>
>> On the other hand, Prolog compilers (in general) are too permissive and often let the user get away with practices that are at best questionable. Calling a non-exported predicate using explicit qualification is one of them.
>>
>> > The idea is that if you use an internal name, you have no right to complain if it changes or disappears. Similarly, for Prolog, if you use a predicate that's not listed as exported (by using the module:predicate form), you have no right to complain. Why would you need anything stronger?
>>
>> That's a slippery slop. It may work for solo code warriors. Or for applications where a single individual can be aware all the details. I.e. when programming in the small. But in organizations with several developers, where new ones are hired and older ones move out but complex applications must be kept running, enforcing sane practices, including not breaking encapsulation boundaries, is a most. Of course, the availability and quality of tools make a difference.
>>
> ​It works at Google with 10s of thousands of engineers. Maybe this is because Google has a strong code-review culture, plus an enforced style guide.

Indeed, good tools + strong process with clear policies are a key part. It becomes more about what's advised/allowed by policies than what level of hacking the programming language allows. In recent workplaces, we end up adapting some of the Google coding guidelines for some mainstream languages.

> There's no need to be "aware of all the details" -- the conventions of only using the public APIs avoids that problem. Someone who wants to break the convention does need to be aware of the details, and is knowingly inviting trouble.
> One exception to the convention is for unit tests... but that's OK because the unit test can be commented that it breaks the convention and is solely for validating some internal-only code.

It's an interesting case. plunit and lgtunit take different approaches here. In the case of lgtunit, there's a debugging control construct that allows something similar with what you get with explicit module qualification. But this control debugging control construct can be can turned off per entity or globally so that encapsulation remains fully enforced.
> To unsubscribe from this group and stop receiving emails from it, send an email to swi-prolog+...@googlegroups.com.

Harry Stoteles

unread,
Jan 26, 2018, 12:16:07 PM1/26/18
to SWI-Prolog
You can add visibility to a Prolog module system. Its not
that difficult. For example you could add the convention
from Java:

    /* public visibility, same as traditional export */
    public member/2.
    member(X, Y) :- ...

    /* package visibility, only seen by modules in the same directory */
    member(X, Y) :- ...

    /* private visibility, only seen by the module itself */
    private member/2.
    member(X, Y) :- ...

A few flags in the predicate data structure of the interpreter do
the job. Plus respecting the actual call site and the flags when
looking up a predicate.

But you need also to be able to break the rules, for example
in the debugger. You might want to set a spy point on a private
predicate.

And meta-programming breaks, you cannot do anymore a
vanilla interpreter. Like for example this code, would need
special permissions:

   solve(true).
   solve((A,B)) :- solve(A), solve(B)
   solve(A) :- clause(A,B), solve(B).

Java solves this that in the reflection package, its possibly
to break the visibility rules, in that one can set suppress
accessibility flag:

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-
To unsubscribe from this group and stop receiving emails from it, send an email to swi-prolog+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages