Page 1 of 1

Accessing supported constants and domains

Posted: 5 May 2019 14:39
by Martin Meyer
Hello Thomas,

VIP's object system is sound and powerful. Its latest add-on "bounded polymorphism" is working already fine in my code.

The only issue about the object system which I think is questionable, is the difference interfaces impose on their type and scope. You have explained it in Interface synonyms.

Actually VIP is already able to do the trick of not making that difference. However it does not do the trick in all places:

Code: Select all

interface objA   constants     const1 : unsigned = 1.   domains     dom1 = dom1(unsigned).   end interface objA   %---   interface objB supports objA end interface objB   %---   interface objC supports objB   constants     const2 : unsigned = 2 * const1. % (a)   domains     dom2 = dom2(dom1, dom1). % (a)   end interface objC   %---   implement main   class predicates     p : (objC::dom1) [used]. % (b) -does not compile- clauses     p(X) :-         stdIO::write(X).   clauses     run() :-         Y = objC::const1, % (b) -does not compile-         stdIO::write(Y).   end implement main
In locations (a) we can refer directly to constants and domains from supported interfaces, while in (b) we cannot.

Are there reasons for the current behavior in (b), which I have not realized/understood yet?

If there is no reason which makes the current behavior necessary, please consider (again) to make code (b) work. I.e. making supported constants and domains accessible through the interface. You have announced addressing the problem in Supports Qualification for Domains. Maybe the change could nicely suit version 10?

Re: Accessing supported constants and domains

Posted: 5 May 2019 18:04
by Thomas Linder Puls
objC can see the names in objA it does not itself define these names.

Re: Accessing supported constants and domains

Posted: 5 May 2019 21:30
by Martin Meyer
Yes.

However why must that imply, we cannot refer via objC to the constants/domains of the interfaces it supports, i.e. to const1 and dom1?

An interface uniformly exhibits predicates/properties originally declared in it as well as those it supports. Why to treat constants/domains differently in this respect? The different treatment lowers the abstraction level of the code which uses the interface. An example:

Code: Select all

interface objA   domains     dom = dom(unsigned).   end interface objA   %===   interface objB supports objA end interface objB   %===   interface objC supports objB   predicates     p : (dom Val).   end interface objC   %---   class objC : objC end class objC   %---   implement objC   clauses     p(Val) :-         stdIO::write(Val).   end implement objC   %===   implement main   class facts     objC : objC := objC::new().   class predicates     q : (objA::dom Val). % refers to objA instead of objC clauses     q(Val) :-         objC:p(Val).   clauses     run() :-         q(objA::dom(123)). % refers to objA instead of objC   end implement main
The main code is about using objC. It is but also necessary to additionally refer to objA in the main code.

If we now want to move dom from objA to objB, the main code needs to be adapted:

Code: Select all

interface objA end interface objA   %===   interface objB supports objA   domains     dom = dom(unsigned). % moved   end interface objB   %===   interface objC supports objB   predicates     p : (dom Val).   end interface objC   %---   class objC : objC end class objC   %---   implement objC   clauses     p(Val) :-         stdIO::write(Val).   end implement objC   %===   implement main   class facts     objC : objC := objC::new().   class predicates     q : (objB::dom Val). % needed adaptation clauses     q(Val) :-         objC:p(Val).   clauses     run() :-         q(objB::dom(123)). % needed adaptation   end implement main
In contrast, if domains defined in an interface would be part of the type that the interface denotes, then we could refer solely to objC in the main code and would not need to adapt it.

Re: Accessing supported constants and domains

Posted: 6 May 2019 23:21
by Thomas Linder Puls
Predicates and properties are only declared in interfaces, their definitions are provided by the objects that support the interface. I.e. predicates and properties belong to the objects. You access them through the object using :.
Domains and constants are declared and defined in the interface itself and belong to that interface. You access them through the interface scope using ::.

Re: Accessing supported constants and domains

Posted: 7 May 2019 1:22
by Martin Meyer
OK. In the Language Reference it reads but:
constants and domains defined in an interface are not part of the type that the interface denotes
But these matters are a question of how to formulate the theory (and you are doing that always concisely very well).

What I want to tell by the thread is however simply pragmatic: It would be useful if we could refer through an interface to the constants/domains of the interfaces which it supports.

Re: Accessing supported constants and domains

Posted: 7 May 2019 10:07
by Thomas Linder Puls
I fully agree that this could be (could have been) done in a different way, but I also think you should take reading into account: When someone reads the code and sees objC::const1 it is quite nice that const1 is actually placed in objC, and not somewhere else.

Furthermore, code like this would be fully legal, but somewhat confusing:

Code: Select all

predicates     ppp : (objA::dom1 A, objC::dom1 C).
(In practice you will most likely not the it in a single declaration, but give the code a couple of maintenance years and then it is very likely that different declarations will use different variants of the domain name.)

Anyway, it is a fundamental property of Visual Prolog that every named entity unique point of declaration which is located inside a named scope (and optionally in a namespace). If the entity is a non-object entity then its full name is <namespace>\<scope>::<name>. In certain contexts parts of the name can be omitted, but it can never be referenced by another name. Because it is never also have another point of declaration.

Finally notice, that given the current way things are now, both objA and objC are allowed to define domains (with functors) and constants with same names. If non-object-related names should be inhereited with supports then these names should also be subject to the same kind of conflict rules as the object-related names. Today they are simply non-conflicting (no declaration conflict, since scope qualifiaction can always be used to resolve usages/references).

Re: Accessing supported constants and domains

Posted: 8 May 2019 3:14
by Martin Meyer
Thank you for answering! No else questions in this post. Just my thoughts/remarks in case you want to read them.

Anyway, it is a fundamental property of Visual Prolog ...
Yes, I see. Changing a fundamental property can turn a lot of things upside down. I can imagine that it could easily produce an unreasonable large amount of work.
Finally notice, that given the current way things are now, both objA and objC are allowed to define domains (with functors) and constants with same names ...
Oh, I had not noticed that before. It means that for constants I can cure the problem 'manually' by simply declaring the constants from the super-interfaces again with same names in the sub-interface:

Code: Select all

interface objA   constants     c : unsigned = 1.   end interface objA   %===   interface objB supports objA   constants     c : unsigned = objA::c.   end interface objB
However for domains it does not work out that way.
When someone reads the code and sees objC::const1 it is quite nice that const1 is actually placed in objC
True. But uniformly referencing to const1 which can be declared in objC or any of its super-interfaces would be even nicer.
Furthermore, code like this would be fully legal, but somewhat confusing ...
Yes. However a sensible example is larger. I can report about the situation in my 'real' code on which I am working currently. But the story is not equally short:

I have a bunch of lexers which are similar to the one in the pfc, but which are customized to languages I want to parse.

The 1st lexer interface is just named lexer (in a different namespace than the one from the pfc). The interface declares constants for three terminals. These are bof_terminal, eof_terminal, and error_terminal ("bof" stands for Begin Of File). The interface is only an abstract root of my lexers, it has no class implementing it.

My 2nd lexer (interface and class) is alphaLexer and it supports lexer. It translates keywords to terminals and additionally declares terminal constants id_terminal and op_terminal. Where id_terminal matches the usual upper- and lower-case IDs and op_terminal matches for example "+", "**", and ":=".

The 3rd lexer is alphaNumLexer supporting alphaLexer. It additionally declares terminal constants number_terminal and decimal_terminal. The fist one matches unsigned integer numerals with arbitrary many digits, the last-mentioned matches unsigned decimal numerals also with arbitrary many digits.

The 4th lexer is strNumLexer supporting alphaNumLexer. It additionally declares terminal constants char_terminal, string_terminal, and further terminals for string literals without ESC-sequence replacement and for unterminated strings.

The 5th lexer is fillerLexer supporting strNumLexer. It additionally declares terminal constant filler_terminal. This terminal matches parts of the source text which are surounded by certain terminals which are input to the constructor of the fillerLexer class. For example filler_terminal can match a leading part of the text which is surounded by bof_terminal and a terminal which matches the keyword "begin".

The lexer interfaces/classes mentioned so far are part of my 'code library' (to accompany the pfc, I put it in namespace mfc).

In my application I have another lexer which contains the terminal constants for the application-specific keywords. It supports one of the above lexers and inherits its class. Let's take the worst case, it supports fillerLexer.

The situation now is, that when I am writing the grammar rules, I need to refer to all the terminal constants and thus I am putting open qualifications for the application-specific lexer and all its super-lexer-interfaces into my code. I have but the feeling, that these are way too many open qualifications.

The alternative to place all terminal constants together in one file does not seem to be really appealing: In the above construction I never need to update an existant file to introduce new terminals (nor for lexers in the library, neither for lexers in an application). Instead I am just adding subtype interfaces, which is lot more elegant.

Re: Accessing supported constants and domains

Posted: 8 May 2019 10:17
by Thomas Linder Puls
Thank you, for the words.

Lexers has more or less disappeared from the surface by being handled by the parser generator. But I recognize the wish/need to create languages/grammars based on other languages/grammars.

The parser generator and the compiler is a clear example of languages that share a common part, because the semantic actions in a grammar is a write Visual Prolog expression. So these two languages could benefit from being defined by as three grammars:
  • the common part
  • the grammar language (which is an extension of the common part)
  • Visual Prolog (which is also an extension of the common part)
But that will have to be another day :-).