dilemma of load files as different objects

Help requests on developing Logtalk applications

Moderator: Paulo Moura

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

dilemma of load files as different objects

Post by shengcer » Thu Aug 15, 2013 4:24 am

I have a set of prolog files in my git master branch. Now I have to branch the git repo for my prolog files, and some modifications will be made on the branch - the object name is unchanged, but the predicate definition might be tweaked. In prolog, I can consult prolog files from master and branch into different modules.

?- consult(master:'master/load.pl') .
?- consult(branch:'branch/load.pl') .

This way I can keep my changes on the branch separated from the master.

I want to achieve the same effect through logtalk. With logtalk, I can transform prolog files into individual objects. Problem is if I do this,

?- logtalk_load('master/load.pl') .
?- logtalk_load('branch/load.pl') .

Since most of the object names are the same across the master and the branch (like what I said, I might just make some very trivial changes on the prolog code residing in the branch), doing above will result in tons of object def conflicts. So this is not an option. I tried this,

?- logtalk_load('master/load.pl') . %% assuming this gives me a big object "everything"
?- create_object(master, [extends(everything)]) .
?- ablish_object ... %% assuming I have the patience to abolish all objects defined through logtalk_load('master/load.pl')
?- logtalk_load('branch/load.pl') . %% this won't break since all predicates defined through logtalk_load('master/load.pl') have been abolished
?- create_object(branch, [extends(everything)]) .

It wont work either, since the created object will complain the object it extends from does not exist.

So my question is is it actually possible to load files with objects of same names but different contents into different objects in logtalk? Thanks!

Paulo Moura
Logtalk developer
Posts: 475
Joined: Sat May 05, 2007 8:35 am
Location: Portugal
Contact:

Re: dilemma of load files as different objects

Post by Paulo Moura » Thu Aug 15, 2013 10:16 am

shengcer wrote:I have a set of prolog files in my git master branch. Now I have to branch the git repo for my prolog files, and some modifications will be made on the branch - the object name is unchanged, but the predicate definition might be tweaked. In prolog, I can consult prolog files from master and branch into different modules.

?- consult(master:'master/load.pl') .
?- consult(branch:'branch/load.pl') .

This way I can keep my changes on the branch separated from the master.

I want to achieve the same effect through logtalk. With logtalk, I can transform prolog files into individual objects. Problem is if I do this,

?- logtalk_load('master/load.pl') .
?- logtalk_load('branch/load.pl') .
You should use the .lgt extension for your Logtalk source files.
shengcer wrote: Since most of the object names are the same across the master and the branch (like what I said, I might just make some very trivial changes on the prolog code residing in the branch), doing above will result in tons of object def conflicts.
Why not use a "master" and a "branch" objects?
shengcer wrote: So this is not an option. I tried this,

?- logtalk_load('master/load.pl') . %% assuming this gives me a big object "everything"
?- create_object(master, [extends(everything)]) .
?- ablish_object ... %% assuming I have the patience to abolish all objects defined through logtalk_load('master/load.pl')
?- logtalk_load('branch/load.pl') . %% this won't break since all predicates defined through logtalk_load('master/load.pl') have been abolished
?- create_object(branch, [extends(everything)]) .

It wont work either, since the created object will complain the object it extends from does not exist.
First you say that the "master/load" file gives you a big object "everything" but then you write that you have to abolish all objects defined through the same file. Can you clarify?
shengcer wrote: So my question is is it actually possible to load files with objects of same names but different contents into different objects in logtalk? Thanks!
You cannot load objects into other objects. The closest thing would be have a "master" and a "branch" namespaces (as in e.g. C++) but these are not currently supported in Logtalk.
Paulo Moura
Logtalk developer

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Thu Aug 15, 2013 1:48 pm

Paulo Moura wrote: You should use the .lgt extension for your Logtalk source files.
my apologize - yes I meant to ignore the extension in "logtalk_load" syntax.
Paulo Moura wrote: Why not use a "master" and a "branch" objects?
OK, so let's say I have a bunch of objects defined, i.e., "apple", "orange", "banana". I want to create a wrapper object to include facts/predicates coming from all these objects,

Code: Select all

% inside master
:- object(master, extends('apple', 'orange', 'banana')) .
:- end_object .
% inside branch
:- object(branch, extends('apple', 'orange', 'banana')) .
:- end_object .
But to load master and branch objects, I will need to load 'apple', 'orange' and 'banana' objects from master and branch, which would cause object definition conflicts. I was trying to abolish apple, orange, banana after I loaded master, so I could load branch later, but that would left master in an unusable state.
Paulo Moura wrote: First you say that the "master/load" file gives you a big object "everything" but then you write that you have to abolish all objects defined through the same file. Can you clarify?
See my reply above - I want to create a big wrapper object which extends from all objects to provide a universal interface answering user's query. Names of the wrapper objects could be different for different git branches, but those small objects have the same name, and I cannot afford to change their names for different git branches which would just make the merge quite a painful experience.
Paulo Moura wrote: You cannot load objects into other objects. The closest thing would be have a "master" and a "branch" namespaces (as in e.g. C++) but these are not currently supported in Logtalk.
Is there any chance that logtalk can support namespace? IMHO, supporting namespace would really make logtalk shine and become an ultimate solution to tackle the single prolog engine restriction placed by jpl. My application is written on top of jpl, which does not allow multiple prolog engines, but I desperately need a way to separate my prolog predicates in different branches. Consulting prolog code as module is one way to do it, but jpl does not allow loading the same file into different modules.
Last edited by shengcer on Thu Aug 15, 2013 2:19 pm, edited 1 time in total.

Paulo Moura
Logtalk developer
Posts: 475
Joined: Sat May 05, 2007 8:35 am
Location: Portugal
Contact:

Re: dilemma of load files as different objects

Post by Paulo Moura » Thu Aug 15, 2013 2:14 pm

It's now more clear what you're trying to accomplish. Logtalk will not have support for namespaces (as in C++) soon so let's look at a different solution. My first suggestion is for you to use a loader file for each branch. E.g. a "master_loader.lgt" and a "branch_loader.lgt". These files would load a wrapper object named after the branch, e.g. "master" and "branch", and all the other objects. The trick would be to rename automatically these other objects, using e.g. the branch name as a prefix, when loading. This can be accomplished by using hook objects (i.e. the term expansion mechanism) for the compilation of the source files. This seems like a doable solution as you wrote that:
I want to create a big wrapper object which extends from all objects to provide a universal interface answering user's query.
If you like and want to try it, I can provide further guidance. Let me know. The Logtalk distribution contains some examples on hook objects.
Paulo Moura
Logtalk developer

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Thu Aug 15, 2013 2:29 pm

Hi Paulo,

Thanks a lot for your quick response! Yes, I am very interested in trying "hook objects".
Paulo Moura wrote:It's now more clear what you're trying to accomplish. Logtalk will not have support for namespaces (as in C++) soon so let's look at a different solution. My first suggestion is for you to use a loader file for each branch. E.g. a "master_loader.lgt" and a "branch_loader.lgt". These files would load a wrapper object named after the branch, e.g. "master" and "branch", and all the other objects. The trick would be to rename automatically these other objects, using e.g. the branch name as a prefix, when loading. This can be accomplished by using hook objects (i.e. the term expansion mechanism) for the compilation of the source files. This seems like a doable solution as you wrote that:
I want to create a big wrapper object which extends from all objects to provide a universal interface answering user's query.
If you like and want to try it, I can provide further guidance. Let me know. The Logtalk distribution contains some examples on hook objects.

Paulo Moura
Logtalk developer
Posts: 475
Joined: Sat May 05, 2007 8:35 am
Location: Portugal
Contact:

Re: dilemma of load files as different objects

Post by Paulo Moura » Thu Aug 15, 2013 2:47 pm

Ok, we can start by the hook object. By using a parametric object, where the only parameter would be the branch name, you can use the same hook object for all branches. The hook object, say, rename/1, would be something like:

Code: Select all

:- object(rename(_Branch),
    implements(expanding)).

    % rename the identifier of individual objects
    term_expansion((:- object(Name)), (:- object(NewName))) :-
        rename(Name, NewName).

    % rename the identifier of the wrapper object
    term_expansion((:- object(Branch, extends(Parents)), (:- object(Branch, extends(RenamedParents))) :-
        parameter(1, Branch),    % be sure that's the wrapper object we're expanding
        rename_all(Parents, RenamedParents).

    rename(Name, NewName) :-
        parameter(1, Branch),
        atom_concat(Branch, '_', NewName0),
        atom_concat(NewName0, Name, NewName).

    rename_all([], []).
    rename_all([Name| Names], [NewName| NewNames]) :-
        rename(Name, NewName),
        rename_all(Names, NewNames).

:- end_object.
Now for contents of the loader files. I will illustrate for a "master_loader.lgt" file:

Code: Select all

:- initialization((
    logtalk_load(rename),
    logtalk_load([
        apple,
        orange,
        banana,
        master    
    ], [hook(rename(master)])
)).
Just be sure that in your wrapper objects you use the syntax:

Code: Select all

:- object(master, extends([apple, orange, banana])).
Now, as long as all queries goes through the wrapper objects, the rename of the individual objects will be transparent, of no consequence, and you can load simultaneously as many branches as you like. Do let us know if the sketched solution works for your application.
Paulo Moura
Logtalk developer

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Thu Aug 15, 2013 4:40 pm

Hi Paulo,

Sorry for the late response - got stuck in a lot of work today. Yes, I will definitely try this, and follow up on this thread for how it works for me.

Thanks,
Sheng
Paulo Moura wrote:Ok, we can start by the hook object. By using a parametric object, where the only parameter would be the branch name, you can use the same hook object for all branches. The hook object, say, rename/1, would be something like:

Code: Select all

:- object(rename(_Branch),
    implements(expanding)).

    % rename the identifier of individual objects
    term_expansion((:- object(Name)), (:- object(NewName))) :-
        rename(Name, NewName).

    % rename the identifier of the wrapper object
    term_expansion((:- object(Branch, extends(Parents)), (:- object(Branch, extends(RenamedParents))) :-
        parameter(1, Branch),    % be sure that's the wrapper object we're expanding
        rename_all(Parents, RenamedParents).

    rename(Name, NewName) :-
        parameter(1, Branch),
        atom_concat(Branch, '_', NewName0),
        atom_concat(NewName0, Name, NewName).

    rename_all([], []).
    rename_all([Name| Names], [NewName| NewNames]) :-
        rename(Name, NewName),
        rename_all(Names, NewNames).

:- end_object.
Now for contents of the loader files. I will illustrate for a "master_loader.lgt" file:

Code: Select all

:- initialization((
    logtalk_load(rename),
    logtalk_load([
        apple,
        orange,
        banana,
        master    
    ], [hook(rename(master)])
)).
Just be sure that in your wrapper objects you use the syntax:

Code: Select all

:- object(master, extends([apple, orange, banana])).
Now, as long as all queries goes through the wrapper objects, the rename of the individual objects will be transparent, of no consequence, and you can load simultaneously as many branches as you like. Do let us know if the sketched solution works for your application.

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Thu Aug 15, 2013 6:20 pm

HI Paulo,

hook object generally works, except two small corrections.

Code: Select all

% in rename.lgt, parentheses of line 9 are not balanced
term_expansion((:- object(Branch, extends(Parents))), (:- object(Branch, extends(RenamedParents)))) :-

Code: Select all

% in master.lgt, I need to append prefix to all objects extended. 
% I guess it is because those small objects are already prefixed with "master"
:- object(master, extends([master_apple, master_orange, master_banana])).
:- end_object .
This is a very good starting point to convert my code fully into logtalk. Thanks a lot for your kind help!
Paulo Moura wrote:Ok, we can start by the hook object. By using a parametric object, where the only parameter would be the branch name, you can use the same hook object for all branches. The hook object, say, rename/1, would be something like:

Code: Select all

:- object(rename(_Branch),
    implements(expanding)).

    % rename the identifier of individual objects
    term_expansion((:- object(Name)), (:- object(NewName))) :-
        rename(Name, NewName).

    % rename the identifier of the wrapper object
    term_expansion((:- object(Branch, extends(Parents)), (:- object(Branch, extends(RenamedParents))) :-
        parameter(1, Branch),    % be sure that's the wrapper object we're expanding
        rename_all(Parents, RenamedParents).

    rename(Name, NewName) :-
        parameter(1, Branch),
        atom_concat(Branch, '_', NewName0),
        atom_concat(NewName0, Name, NewName).

    rename_all([], []).
    rename_all([Name| Names], [NewName| NewNames]) :-
        rename(Name, NewName),
        rename_all(Names, NewNames).

:- end_object.
Now for contents of the loader files. I will illustrate for a "master_loader.lgt" file:

Code: Select all

:- initialization((
    logtalk_load(rename),
    logtalk_load([
        apple,
        orange,
        banana,
        master    
    ], [hook(rename(master)])
)).
Just be sure that in your wrapper objects you use the syntax:

Code: Select all

:- object(master, extends([apple, orange, banana])).
Now, as long as all queries goes through the wrapper objects, the rename of the individual objects will be transparent, of no consequence, and you can load simultaneously as many branches as you like. Do let us know if the sketched solution works for your application.

Paulo Moura
Logtalk developer
Posts: 475
Joined: Sat May 05, 2007 8:35 am
Location: Portugal
Contact:

Re: dilemma of load files as different objects

Post by Paulo Moura » Thu Aug 15, 2013 6:36 pm

shengcer wrote:HI Paulo,

hook object generally works, except two small corrections.

Code: Select all

% in rename.lgt, parentheses of line 9 are not balanced
term_expansion((:- object(Branch, extends(Parents))), (:- object(Branch, extends(RenamedParents)))) :-
I wrote the code directly on the post submission form. I'm happy that's the only typo :-)
shengcer wrote:

Code: Select all

% in master.lgt, I need to append prefix to all objects extended. 
% I guess it is because those small objects are already prefixed with "master"
:- object(master, extends([master_apple, master_orange, master_banana])).
:- end_object .
Hum? That should have been taken care automatically by the second term_expansion/2 clause in the rename/1 hook object. Check your loader file that you're also compiling the wrapper objects such as "master" using the hook/1 option.
shengcer wrote: This is a very good starting point to convert my code fully into logtalk. Thanks a lot for your kind help!
Enjoy programming in Logtalk.
Paulo Moura
Logtalk developer

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Thu Aug 15, 2013 6:49 pm

Paulo Moura wrote: Hum? That should have been taken care automatically by the second term_expansion/2 clause in the rename/1 hook object. Check your loader file that you're also compiling the wrapper objects such as "master" using the hook/1 option.
You are right, I used to write master.lgt like this,

Code: Select all

:- object(master, extends(apple, orange, banana)) .
:- end_object .
After I transformed it into

Code: Select all

:- object(master, extends([apple, orange, banana])) .
:- end_object .
Everything works. Thanks!

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Sun Aug 18, 2013 3:47 pm

what about if there is an object needs to refer to the predicates defined in the other object? For example,

Code: Select all

  % in file apple.lgt at git branch "gala"
  :- object(apple) .
     :- public(color/1) .
     color(red) .
  :- end_object .

  % in file apple.lgt at git branch "macintosh"
  :- object(apple) .
    :- public(color/1) .
    color(white) .
    color(silver) .
  :- end_object .

  % then there is a file sales.lgt needs to refer to apple::color/1 .
  :- object(sales) .
    avail_apple_color(V) :-
      apple::color(V) .  
  :- end_object
how can I make sales object refer to apple.lgt within the same branch? I tried to use "apple::color" as above, but it did not work since the apple object had been renamed by the hook object.

Paulo Moura
Logtalk developer
Posts: 475
Joined: Sat May 05, 2007 8:35 am
Location: Portugal
Contact:

Re: dilemma of load files as different objects

Post by Paulo Moura » Sun Aug 18, 2013 4:10 pm

One solution would be to add clauses for the goal_expansion/1 predicate to the hook object. But that requires you to keep track and check which objects have been renamed after the branch name. Maybe modify the hook object term_expansion/2 clause that expands the open object directive for the wrapper object to store locally (using a dynamic predicate) which objects are extended by the wrapper object. Something like:

Code: Select all

    :- private(renamed_object_/2).
    :- dynamic(renamed_object_/2).

    % rename the identifier of the wrapper object
    term_expansion((:- object(Branch, extends(Parents)), (:- object(Branch, extends(RenamedParents))) :-
        parameter(1, Branch),    % be sure that's the wrapper object we're expanding
        rename_and_remember_all(Parents, RenamedParents).

    rename_and_remember_all([], []).
    rename_and_remember_all([Name| Names], [NewName| NewNames]) :-
        rename(Name, NewName),
        assertz(renamed_object_(Name, NewName)),
        rename_and_remember_all(Names, NewNames).

    goal_expansion(Name::Message, NewName::Message) :-
        renamed_object_(Name, NewName).
Wither this is a working and scalable solution depends of course on the details of your application.
Paulo Moura
Logtalk developer

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Mon Aug 19, 2013 9:22 pm

Could you explain more on "goal_expansion"? Does it always direct the predicate coming to object "Name" to the object "NewName" ?
Paulo Moura wrote:One solution would be to add clauses for the goal_expansion/1 predicate to the hook object. But that requires you to keep track and check which objects have been renamed after the branch name. Maybe modify the hook object term_expansion/2 clause that expands the open object directive for the wrapper object to store locally (using a dynamic predicate) which objects are extended by the wrapper object. Something like:

Code: Select all

    :- private(renamed_object_/2).
    :- dynamic(renamed_object_/2).

    % rename the identifier of the wrapper object
    term_expansion((:- object(Branch, extends(Parents)), (:- object(Branch, extends(RenamedParents))) :-
        parameter(1, Branch),    % be sure that's the wrapper object we're expanding
        rename_and_remember_all(Parents, RenamedParents).

    rename_and_remember_all([], []).
    rename_and_remember_all([Name| Names], [NewName| NewNames]) :-
        rename(Name, NewName),
        assertz(renamed_object_(Name, NewName)),
        rename_and_remember_all(Names, NewNames).

    goal_expansion(Name::Message, NewName::Message) :-
        renamed_object_(Name, NewName).
Wither this is a working and scalable solution depends of course on the details of your application.

Paulo Moura
Logtalk developer
Posts: 475
Joined: Sat May 05, 2007 8:35 am
Location: Portugal
Contact:

Re: dilemma of load files as different objects

Post by Paulo Moura » Mon Aug 19, 2013 9:45 pm

shengcer wrote: Could you explain more on "goal_expansion"?
See the Reference Manual page on the predicate:
http://logtalk.org/manuals/refman/metho ... sion2.html

Also the "expanding" and "hooks" examples in the Logtalk distribution.
shengcer wrote: Does it always direct the predicate coming to object "Name" to the object "NewName" ?
As long as you use an explicit message sending goal that unifies with the term Object::Message (and Object is a renamed object).
Paulo Moura
Logtalk developer

shengcer
Posts: 21
Joined: Wed Aug 14, 2013 7:54 pm

Re: dilemma of load files as different objects

Post by shengcer » Wed Aug 21, 2013 2:13 pm

OK, so I have decided to use "goal_extension" to work around this issue. I am wondering it is possible to

1. supply multiple hook objects to logtalk_load? I tried this,

Code: Select all

logtalk_load([
.....
], [hook(hook_obj1), hook(hook_obj2)]) .
but it does not work.

2. extend a parametric object? Documentation does not mention it.

Thanks,

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest