Hi Jan,
> On 12 Jun 2018, at 10:05, Jan Wielemaker <
j...@swi-prolog.org> wrote:
>
> On 11/06/18 15:49, Paulo Moura wrote:
>> Hi Jan,
>>
>> Let me start by saying that I'm fully aware (as other long time Prolog programmers are) that several of the module system issues we are discussing are inherited and fixing them would break havoc backwards compatibility.
>>
>>> On 11 Jun 2018, at 13:05, Jan Wielemaker <
j...@swi-prolog.org> wrote:
>>>
>>> On 06/11/2018 11:37 AM, Paulo Moura wrote:
>>>> Steroids have usually a negative connotation... But such
>>>
>>> Steroids to me have no negative connotation :) I simply meant a system
>>> that provides much more than the module systems as we know them. Whether
>>> that is good or bad depends on what you need. If you need the stuff
>>> Logtalk offers it is great. If you don't, it is not. It does come at a
>>> price. It enlarges the footprint,
>>
>> Is the footprint (of loading Logtalk) that significant nowadays? Using the included script for embedding Logtalk we get:
>>
>> $ ll
>> total 632
>> -rw-r--r-- 1 pmoura staff 322284 Jun 11 13:56 logtalk.qlf
>>
>> This QLF files includes both the compiler and runtime.
>
> Thanks that current resources are zip :)
The switch to zip is a great improvement indeed. Thanks!
> I must admit that the memory
> usage impact is small. Startup of a simple state goes from 12ms to about
> 20ms. That is significant, but relevant to a small minority of
> applications such as using Prolog/Logtalk as traditional CGI script.
>
>>> it makes some of the development tools
>>> work less transparently or not at all (in return you get some Logtalk
>>> development tools),
>>
>> Some? :-) Logtalk provides a full set of developer tools. A good summary with usage examples is available at:
>>
>>
https://logtalk.pt/tools/
>>
>> Which tool you find missing? But you can also use the SWI-Prolog graphical tracer and graphical profiler, thanks for the hooks that you have in SWI-Prolog.
>
> Yes, but the Edit menu in the graphical profiler doesn't work, nor
> edit/1 at the prompt.
I will look into the graphical profiler edit menu issue. But for edit/1 at the top-level I get:
?- [aliases(loader)].
% [ /Users/pmoura/logtalk/examples/aliases/aliases.lgt loaded ]
% [ /Users/pmoura/logtalk/examples/aliases/loader.lgt loaded ]
% (0 warnings)
true.
?- edit(aliases).
Please select item to edit:
1 <file> aliases
2 <loaded file> 'logtalk/examples/aliases/aliases.lgt'
3 <file> aliases
Your choice?
Sounds good to me. Can we improve on it?
> I guess that is fixable using more hooks and
> implementation thereof in Logtalk. Similar issues exist with the low
> level tools such as jiti_list/1, vm_list/1, etc. to understand the
> program at all its levels. I did some code analysis at Kyndi for Logtalk
> code and these are the small things you stumble on.
They are indeed some small issues and I'm happy to work with you in fixing them.
> Of course I'm an
> outlier as I know all the SWI-Prolog tools very well and not the Logtalk
> replacements.
>
>>> it makes loading code slower as it implements (I
>>> think) a three-phase compiler (two phases compiling Logtalk, one loading
>>> the result), whereas SWI-Prolog has a single-phase compiler.
>>
>> 14:04 $ ll large.*
>> -rw-r--r--@ 1 pmoura staff 1072336 Mar 3 2013 large.lgt
>> -rw-r--r--@ 1 pmoura staff 1072324 Nov 7 2013
large.pl
>>
>> The Logtalk file simply encapsulates the contents of the
large.pl file inside an object. The contents itself is one of the big Unicode tables.
>>
>> ?- time(true).
>> % 3 inferences, 0.000 CPU in 0.000 seconds (72% CPU, 300000 Lips)
>> true.
>>
>> ?- time(load_files('
large.pl')).
>> % 827,212 inferences, 0.137 CPU in 0.144 seconds (95% CPU, 6045899 Lips)
>> true.
>>
>> ?- time(logtalk_load('large.lgt')).
>> % [ /Users/pmoura/Desktop/large.lgt loaded ]
>> % (0 warnings)
>> % 1,596,437 inferences, 0.360 CPU in 0.434 seconds (83% CPU, 4434658 Lips)
>> true.
>>
>> So, roughly twice the number of inferences and 2.5x the compilation time. This may have some practical significance for large data files (this example can be considered a data file, albeit a small one). But giving Logtalk's make tool and how easy is to precompile Logtalk code into QLF files, that's only an issue the first time you compile a large file. Then again, why encapsulate large data in the first place?
>
> The time difference is about what I expected. Why you use Logtalk for
> data?
I *don't*. This is an artificial benchmark constructed just to do an apples to apples (or oranges to oranges for you Linux users ;-) comparison of compiler overhead.
> I suspect many Logtalkers will do so.
Not that I'm aware. In fact, in most cases with big data files that I worked on for industrial clients (or where I gave feedback), the data files are usually plain Prolog files. When SWI-Prolog was used, those data files were precompiled into QLF files.
> Note that compiler speed
> does matter. At least I have a couple of users that frequently complain
> about long compile times. There are people with really large programs
> and not only for big data files ...
Isn't that more of any issue of proper modularization and use of make tools (which are available in both SWI-Prolog and Logtalk)? If everytime you make a change you need to recompiled everything, you have a *build* problem and there the compiler performance may indeed be a factor.
>>> It requires
>>> temporary files, which is typically no real problem but is on some
>>> build and deployment scenarios.
>>
>> Logtalk default scratch directory can be set to /tmp. Which operating-systems are people using that don't provide that or an equivalent? For deployment, the usual solution is to precompile and embed Logtalk and the Logtalk applications, not to be compiling application code in deployment scenarios (an observation which is also valid for Prolog itself).
>
> I recently understood that the nodes of some of the big tech companies
> run on a completely read-only filesystem.
That seems to be common indeed for security reasons.
> Now as saved states seem to
> work with recent Logtalk versions this is possible.
See the embedding script for SWI-Prolog distributed with Logtalk:
https://github.com/LogtalkDotOrg/logtalk3/tree/master/scripts/embedding
Try the saved state examples at:
https://github.com/LogtalkDotOrg/logtalk3/blob/master/scripts/embedding/SCRIPT.txt
> Some people are a
> bit scared about using saved states that indeed come with some problems
> wrt. initialization and access to resources such as foreign libraries.
Complex cases most likely still require some customization, fine tuning of the build infrastructure. But both Logtalk and SWI-Prolog got significantly better recently for deploying applications :-)
>>> As we have seen, making Logtalk
>>> compatible with SWISH is probably a difficult task.
>>
>> There's for quite some time a experimental Logtalk pack that works with SWISH. Douglas Miles as playing with it some time ago. Myself, have not looked into it at detail. Happy to revisit it.
>
> Good to know. I thought the problem of compiling Logtalk objects to
> (temporary) modules was not solved. Curious how Douglas managed to
> hack around that. He's surely very creative :)
I provided Douglas with a version of the pack that loads Logtalk into a module (other than "user"). No idea if he played with hacking it to use a *temporary* module, however.
>>>> characterization is misleading considering that Logtalk provides*basic*
>>>> features for an encapsulation mechanism that are not provided by
>>>> modules. That includes (short list):
>>>>
>>>> - separation between interface and implementation (protocols are first-class
>>>> entities)
>>>
>>> That can indeed be quite useful. It can be done in plain (modularized)
>>> Prolog, but it is a bit hacky and consistency (verifying the
>>> implementation does indeed implement the interface) doesn't come
>>> for free.
>>
>> Is not that is just hacky with modules. The current and recommended practice with Prolog modules is that *exported* predicates should not clash. I.e. modules, as encapsulation units, in practice are there only to prevent clashes in *private* predicates. You likely remember the discussion over which module had the right to export a transpose/2 predicate some years ago? Or consider module libraries such as "avl" or "rbtrees" and other implementing the same data structure but with completely different interfaces. Contrast that with Logtalk where you have a dictionary protocol and several objects implementing it, which makes switching between alternative implementations trivial. Being able to define a protocol/interface and provide multiple implementations is one of the most basic features for an encapsulation mechanisms. Its absence in module systems twists practice and leads to non scalable, brittle solutions.
>
> It would probably be good practice to implement the same API for modules
> that implement the same abstract data type using different technology.
> That also works in Prolog. It seems pretty unlikely that someone wishes
> to load rbtrees and assoc into the same module and in the case they wish
> to do so, using :- use_module(library(assoc),[]) and assoc:empty_map(Map)
> or something like that would work. All Logtalk offers for this is the
> notion of a protocol. That is easily defined in Prolog. Just introduce
> a syntax to define a protocol such as
>
> :- protocol(map,
> [ empty_map/1,
> ...
> ]).
>
> And a term expansion that expands :- implements_protocol(map). into a
> module header and you are done.
Not really done. Logtalk objects can implement any number of protocols. You can only have a single module header. Also, a protocol doesn't define just normal predicates but also e.g. meta-predicates. Thus, you need to pass more than just predicate indicators. Your simple example is simplistic and needs (much) more to be a practical solution.
multifile/1 does indeed more than you want. Is not just overkill. It's the wrong solution. The semantics are different. Certainly you are not advocating making any predicate declared in a protocol/interface hack for modules a multifile predicate?
> AFAIK the ISO
> proposal also separates the interface from the implementation. This
> suggests the idea lives, but apparently nobody bothered implementing
> it in a module system while this is of course trivial.
The ISO module standard do not support multiple implementations per interface. One must wonder why they even bother to publish it.
>>>> - simple scope rules for all flags (flag directives are always local to
>>>> their scope)
>>>
>>> Flags (as in current_prolog_flag) are a nightmare.
>>
>> A mostly *inherited* nightmare :(
>>
>>> It is a single
>>> mechanism that affects the compiler and runtime behavior. Flags that
>>> affect the compiler should probably have lexical scoping. Some do in
>>> SWI-Prolog but inconsistently either due to lazyness or compatibility
>>> concerns. Flags that affect runtime behaviour are hard to handle and
>>> having threads makes it even harder. If you have a golden solution I'm
>>> glad to hear about it :) My general opinion is "don't touch them" with
>>> as only exception to touch them at program startup to deal with
>>> portability issues.
>>
>> The solution I use for Logtalk flags could certainly be used for Prolog flags by it would break backwards compatibility :(
>
> Well, lexical scoping for flags that affect the compiler is easy. Scoped
> switching between the different occurs_check values is a different matter.
> Are you saying you can do that?
The occurs_check flag is a Prolog flag, not a Logtalk flag. Note that Logtalk acts as a pure add-on. It doesn't change Prolog builtin features (that would kill portability but would also cause confusion and havoc even when using a single backend Prolog system).
>>>> - enforcement of encapsulation (which you may able to do without iff you
>>>> have strong team discipline)
>>>
>>> That is a choice. A good team should have coding guidelines. Not
>>> breaking encapsulation should be one of these rules. IMO it is pretty
>>> useful that these rules are not enforced by default during development
>>> and to hack around reusing legacy code. It would not be very hard to
>>> enforce them and given library(prolog_codewalk) it is just a few lines
>>> to write a checker for cross-module calls to private predicates. This
>>> can be fooled, although it is also quite easy to locate calls for which
>>> the target cannot be deduced at compile time.
>>>
>>>> The point is, you don't need to be doing programming in the large to
>>>> benefit. Logtalk may look like, as you wrote, a module system on
>>>> steroids but only because module systems are in reality a prefixing
>>>> mechanism stretching to double as a rather limited encapsulation mechanism.
>>>
>>> I don't think that is the main value of Logtalk vs module systems. It is
>>> too easy to do that in a module system.
>>
>> Above you have several examples that contradict that. As I wrote, this was just a short list. I could easy include better term-expansion, message printing, question asking mechanisms. Or parametric entities. Or optionals and expecteds libraries (that take full advantage of parametric objects), ...
>
> I think I argued that a lot of this stuff is easily done in the context
> of modules.
For some simplistic scenarios, maybe. You will not get very far with them. There are also a good number of fine details with semantic implications that we are not even discussing here.
There have been people toying and arguing for years about how to expand module systems (reinventing parts of Logtalk in the process). Nothing of practical consequence have ever been done. One of the main reasons is that a predicate prefixing mechanism is the wrong basis to start with. There's also a big difference between talking about it and actually delivering it as a mature system that other can use.
It should be noted that the very same things that people find convenient when writing small programs are often the ones that get them in trouble when programs get more complex. Case in point, we see regularly in this mailing list how the consequences of implicit qualification in its illusory simplicity causes perplexity on program behavior and leads to length discussions of people trying to understand what's happening and how to fix it. Logtalk, despite being more featured, leads to easier to understand code as it's not based on a prefixing mechanism acting under the covers.
> You may of course ask why it hasn't been done.
Are you kidding? After all the work I did on Prolog standardization? :-D
> Well, quite a
> bit has been done by some Prolog system, e.g., term-expansion in Ciao
> and ECLiPSe, message printing in Quintus and SWI-Prolog, question asking
> in SWI-Prolog.
I wrote better, not new :-) Logtalk term-expansion mechanism for one is a significant improvement. We discuss it in the past in this mailing list.
> I'm not aware of parametric entities as a standard Prolog
> facility. I've seen a talk at ICLP quite a few years proposing this for
> modules. Surely there are applications doing this type of stuff as it is
> quite natural in Prolog.
Parametric entities are a wonderful versatile concept. For for several application scenarios, see e.g:
@inproceedings{pmoura09d,
author = {Paulo Moura},
title = "{Knowledge Representation Using Logtalk Parametric Objects}",
booktitle = {Proceedings of the 18th International Conference on Applications of Declarative Programming and Knowledge Management (INAP)},
editor = "Salvador Abreu and Dietmar Seipel",
publisher = "University of \'Evora, Portugal",
pages = {225-240},
month = nov,
year = 2009
}
> One thing that is hard is to consider a module as a set of methods and
> perform clean inheritance on that.
Indeed. Btw, Logtalk supports multiple inheritance, multiple instantiation, *and* a mechanism to override the default predicate lookup algorithm in case of inheritance conflict. And yes, multiple inheritance can be clean with a bit of care. But it can also blow your hands.
>>> The main value lies IMO in more
>>> sophisticated mechanisms for code reuse such the notion of interfaces,
>>> calling self/super methods, etc. All these things can be done in
>>> modularized Prolog, but having it all available in a consistent, nicely
>>> documented and well tested package is a clear win.
>>
>> Thanks.
>>
>>> I find myself
>>> typically only wishing for tiny pieces of Logtalk for which I prefer
>>> stretching Prolog a little over switching to Logtalk.
>>
>> Logtalk was *designed* to extend Prolog, not to replace it. It's not a *switch to*, it's a *use with*. Logtalk does provide an alternative to Prolog modules but it happily co-exists with them. Is not a "either" choice. In fact, modules can send messages to objects with zero overhead (in SWI-Prolog and YAP) Objects can call module predicates also have zero overhead.
>
> Is that new?
You mean the performance bits? That are indeed some performance improvements in the recent years. But Logtalk main design goals and choices are the same as when first released back in 1998.
> I always liked the idea that you could do
>
> :- use_module(library(logtalk)).
>
> :- object(...)
> ...
>
> etc. I've always understood there was a Logtalk executable as swi-lgt.
> Since years I have a little load library to load Logtalk dynamically
> which is notably practical if I need to run SWI/Logtalk under gdb
> debugger and thus need the raw executable rather than a shell script.
You can still do that. Look into the sources of the swilgt.sh script.
> I know it is non-negotiable due to your portability requirements, but I
> also would like to see most of Logtalk a shallow and possibly modular
> wrapper around Prolog's modules.
Not going to happen: a predicate-prefixing mechanism is simply the *wrong* foundation.
> That would make the system more
> transparent and invites using Logtalk for things it is good at rather
> than an alternative module syntax.
Is not an alternative syntax. Logtalk doesn't use a predicate-prefixing mechanism. That have fundamental consequences on semantics.
> I still claim that modules solve most
> of the problems needed to deploy Prolog for large programs.
It doesn't. Not the least due to current recommended practices.
I regularly talk with people that think that because they are writing big and complex applications they are doing programming in the large. To be blunt, anyone that can oversee all that is going on in an application is, be definition, programming in the small.
> I do agree
> it neglects some issues and poor standardization doesn't do much good
> either. Still, module incompatibility is a minor problem for porting
> Prolog in my experience. (Lack of) e.g., tabling, attributed variables,
> mutable objects, threads, foreign interface, indexing, etc. are way
> harder to deal with.
Writing portable code beats hands down porting code. But, as you say, there are too many things that have no portable solution. The challenge is often in trying, if possible, to modularize the non-portable bits. Not easy :(
Cheers,
Paulo