Discussions related to Visual Prolog
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Generic predicates

Unread post by Peter Muraya »

Hi,
Does Visual Prolog supports or plan to support generic predicates?
I'm using the 7.4 version and I have a section of my code that looks rather untidy because it has a recurrent pattern that, I think, could be neatly solved using generic predicates. The following 2 sample codes illustrate what I mea,.

1. Sample of code with a recurrent pattern
----------------------------------------------

Code: Select all

interface integration      predicates         get_sheet:()->sheet.         try_sheet:()->sheet determ.           get_text->text.         try_text->text determ.           .... More similar definitions  below........ end interface integration    implement integration     /*     Returns the sheet that in this context. Throws an exception if one is not found*/     get_sheet()=Sheet:-           try_sheet()=Sheet,!.     get_sheet()=_:-          error::raise(Cursor, "Unable to find a sheet in this context").       /*     Returns a sheet in this context if any; fails if there is none*/     try_sheet()=Sheet:-          /*          Get any ancestor of this context*/          Ancestor = ancestors(This),          /*          See if the ancestor can be converted to a sheet*/          Sheet = tryconvert(sheet, Ancestor),!.        /*     Returns the text that is in this context. Throws an exception if none is not found*/     get_text()=Text:-           Text = try_text(),!.     get_text()=_:-          error::raise("Unable to find a text in this context").       /*     Returns text in this context if any; fails if there is none*/     try_text()=Text:-          /*          Get any ancestor of this context*/          Ancestor = ancestors(This),          /*          See if the ancestor can be converted to text*/          Text = tryconvert(text, Ancestor),!.        ....   end implement integration
This is how I think generic predicates might address this issue
---------------------------------------------------------------------------------------

Code: Select all

interface integration{@Type}     predicates           get_{@Type}()->{@Type}.           try_{@Type}()->{@Type} determ. end interface integration   interface integration{@Type}    /*     Returns the @Type  that is in this context. Throws an exception if none is not found*/     get_{@Type}(_Cursor)=Type:-           Type = try_{@Type}(),!.     get_{@Type}()=_:-          error::raise("Unable to find a ", xxx(@Type), " in this context").       /*     Returns a text import file in this context if any; fails if there is none*/     try_{@Type}()=Type:-          /*          Get any ancestor of this context*/          Ancestor = ancestors(This),          /*          See if the ancestor can be converted to text*/          Type = tryconvert({@Type}, Ancestor),!. end interface integration
In the absence of generic predicates, what would be the best approach for solving this untidy code problem?
Mutall Data Management Technical Support
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

We do not have any plans for such kind of generic stuff.

However, there are already constructions in the language that can lower the number of "recurrent patterns".

For example you can implement your get_xxx predicates using a common predicate instead of repeating the pattern again.

Code: Select all

clauses     get_sheet() = get(try_sheet, "sheet").     get_text() = get(try_text, "text").     % ... other get_xxx predicates   class predicates     get : (core::function_dt{Type} TryGet, string Kind) -> Type Value. clauses     get(TryGet, _Kind) = Value :-         Value = TryGet(),         !.       get(_TryGet, Kind) = _ :-         error::raise(string::format("Unable to find a % in this context", Kind)).
The try_xxx predicates are a bit more difficult, because it is a type you are using to test with.

I assume that "sheet" and "text" are interfaces, using anonymous predicates you can write like this:

Code: Select all

clauses     try_sheet() = tryGetAncestor({ (A) = tryConvert(sheet, A)).     try_text() = tryGetAncestor({ (A) = tryConvert(text, A)).     % other try_xxx predicates   class predicates     tryGetAncestor : (core::function_dt{object, Type} TryConvert) -> Type Value determ. clauses     tryGetAncestor(TryConvert) = Value,          Value = TryConvert(ancestors(This)),          !.
We do have plans of giving more access to types, which could be used to avoid the anonymous predicates in the last case.

But we do not have any plans in the direction of introducing templates or macro.
Regards Thomas Linder Puls
PDC
Martin Meyer
VIP Member
Posts: 328
Joined: 14 Nov 2002 0:01

Unread post by Martin Meyer »

Hi Peter,

why do you want to have two (or even more) predicates get_text and get_sheet resp. try_text and try_sheet at all, rather than better using respectively one, which serves all purposes by means of polymorphism? It can be done like in below code.

Regards
Martin

Code: Select all

interface folder{@Type}       predicates         get_item : () -> @Type.           tryGet_item : () -> @Type             determ.   end interface folder   class folder{@Type} : folder{@Type} end class folder   implement folder{@Type}       clauses         get_item() = Item :-             Item = This:tryGet_item(),             !.         get_item() = _ :-             exception::raise_error("Folder is empty").       clauses         tryGet_item() = _ :-             exception::raise_overloadingExpected(). %or just put "fail" here.   end implement folder   %---   class text_folder : folder{string} end class text_folder   implement text_folder     inherits folder{string}       clauses         tryGet_item() = "I am a sample text".   end implement text_folder   %---   class sheet_folder : folder{sheet}     open core       domains         sheet = sheet(positive Width_in_mm, positive Height_in_mm).   end class sheet_folder   implement sheet_folder     inherits folder{sheet}       constants         din_a4 : sheet = sheet(210, 297).       clauses         tryGet_item() = din_a4.   end implement sheet_folder           ...       class predicates         test : ().     clauses         test() :-             Folder1 = text_folder::new(),             Folder2 = sheet_folder::new(),             useFolder(Folder1),             useFolder(Folder2).       class predicates         useFolder : (folder{Type}).     clauses         useFolder(IntegrationObj) :-             Item = IntegrationObj:get_item(),             stdIo::write(Item, "\n").
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Generic predicates

Unread post by Peter Muraya »

Thank Thomas and Martin.
Thank you Thomas for pointing me in the direction of function_dt domain. I have seen it defined in the Prolog foundation core class but did not understand it well enough to see how it would help in this problem. I will now look at the untidy code problem from this perspective.

Martin, I have tried working with generic data classes and interfaces to solve this kind of problem before. I don't quite remember the exact reason I discontinued using them. Your code has encouraged me to re-visit them. Thanks.
Mutall Data Management Technical Support
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Generic predicates

Unread post by Peter Muraya »

Hi Martin and Thomas,
I have re-visited the generic predicate/untidy repetitive code issue in light of the following approaches:-
(a) the polymorphism/parametrization as suggested by Martin and
(b( the anonymous predicates as suggested by Thomas

In my earlier attempt, I discontinued using generic interfaces and classes because I realized they don't
apply to class predicates. And re-doing my code to respect that fact was more work than I was prepared
to put in. My interest was rekindled by the need to make my code neater (and so easier to maintain), as
well as the need to let the compiler highlight potential problems in my code earlier than at run-time. Excessive
use of convert and tryconvert was beating that purpose.

Check the code below to see how I have used the 2 of your suggestions to come up with a design pattern
that I'm planning to adopt widely.

However, arising out of the coding experience, I have type compatibility issues that I would like to
be clearer about than I am now.

1) I thought that the following 2 definitions are the same? But the compiler says
'Impossible to determine the type of the term' on the 2nd definition.

new:(core::function_dt{object, @Type})
new:(core::function_dt{X, @Type})
2) Before using the anonymous predicate at tis point, I intuitively used the remarked code and got the following
compiler error.
The expression has type 'context', which is incompatible with the type '@Type'
Can you explain why it is illegal to do so? Is this related to Thomas' point about meant that
"We do have plans of giving more access to types, which could be used to avoid the anonymous predicates in the last case"

3) Thomas assumed that sheet and text were interfaces. And yes they are. Why was such an assumption
important?

Code: Select all

interface sheet     properties           name:string. end interface sheet   interface context      predicates           ancestors:()->context nondeterm. end interface context   interface retriever{@Type}      properties           name:string.      predicates           get:(context)->@Type.           tryget:(context)->@Type nondeterm. end interface retriever   class retriever{@Type}:retriever{@Type}      constructors           new:(core::function_dt{object, @Type} TryConvert, string Name).                       /*..................issue 1*/ end class   implement retriever{@Type}      facts           tryconverter: core::function_dt{object TryConvert, @Type}:=erroneous.           name:string:=erroneous.      clauses           new(TryConvert, Name):-                tryconverter := TryConvert,                name:=Name.             get(Context)=Object:-                Object=tryget(Context),!.           get(_Context)=_:-                exception::raise_error("No ", name, " found").             tryget(Context)=Object:-                Ancestor = Context:ancestors(),                %Object = tryconvert(@Type, Ancestor).                                          /*...............issue 2*/                Object = tryconverter(Ancestor).   end implement retriever   implement main      clauses           run():-                console::init(),                /*                Create a contextual tree (in which one of the branches is ether a sheet, text or none of these)*/                Context = create_contextual_tree(),                /*                Define an anonymous function for converting to an object to a sheet class*/                TryConvert = {(A)=tryconvert(sheet, A)},                                                  /*...................issue 3*/                /*                Use the anonymous function to create a new (sheet) retriever*/                Retriever = retriever::new(TryConvert, "sheet"),                /*                Now retrieve the sheet that is in the given context, throwing an exception if it is not found*/                Sheet = Retriever:get(Context),                /*                Output the name of the sheet*/                stdio::write("The sheet name is ", Sheet:name).            class predicates               create_contextual_tree:()->context.          clauses                resolve create_contextual_tree externally   end implement main   goal     mainExe::run(main::run).
Mutall Data Management Technical Support
Martin Meyer
VIP Member
Posts: 328
Joined: 14 Nov 2002 0:01

Unread post by Martin Meyer »

I am trying to answer your first two questions. Thomas may correct me, when am wrong:

to 1) object and X (in the place you used it) have different meanings:

Tutorial http://wiki.visual-prolog.com/index.php ... nd_Objects says >>object is an implicitly defined interface that has no contents (i.e. no predicates). Any object supports the object interface directly or indirectly, so any object has type object. And therefore object is the super type of all object types.<< So in short, object is a certain type.

X means a local polymorphic domain. It is the most general such domain, as the type variable X matches any arbitrary type. For example tuple{X, X} would be a more special local polymorphic domain.

When issueing a call to a predicate, which has an argument of a local polymorphic domain, the compiler must be able to determine the actual type of the term, which is supplied to the argument. When the compiler cannot derive the type from the call's context, then it will throw an error like 'Impossible to determine the type ...'

When you replace core::function_dt{object, @Type} in your code by core::function_dt{X, @Type}, then the compiler cannot determine the type of A in tryConvert(sheet, A)} anymore. Before the replacement however it could determine the type of A: it was object.

to 2) When replacing Object = tryConverter(Ancestor) by Object = tryConvert(@Type, Ancestor), compiler complains, because in tryConvert and convert it is not legalt to specify the return domain as a polymorphic domain. The Language Reference http://wiki.visual-prolog.com/index.php ... es#convert teaches: >>returnDomain must be a name of built-in Visual Prolog domain, an interface domain, a name of such user defined domain that is synonym to one of built-in Visual Prolog domains, a numeric domain, binary and pointer domains. The domain name returnDomain must be specified at compile-time, i.e. it cannot come from a variable.<<

to the code in general) Yet I cannot figure out exactly, what you want to do in the code, you posted lastly, and thus cannot come up with a version, how I would write it.

Many regards
Martin
Peter Muraya
VIP Member
Posts: 147
Joined: 5 Dec 2012 7:29

Unread post by Peter Muraya »

Excellent, Martin. This is very clear now.
Mutall Data Management Technical Support
Post Reply