Discussions related to Visual Prolog
dcgreenwood
Posts: 24
Joined: 16 Jan 2021 1:33

Unifying domains?

Unread post by dcgreenwood »

I think the answer to my question is no, but I'll ask anyway.

Is there a way to declare two elements of a domain as being equivalent?

Code: Select all

domains mydomain : model; friendlymodel; enemymodel.   class facts modelFact : (mydomain Modeltype, string Name).   clauses setup():- assert(modelFact(friendlymodel,"Name1")), assert(modelFact(enemymodel,"Name2")).   class predicates findModelName : (mydomain Modeltype, string Name[out). clauses findModelName(Modeltype,Name):- modelFact(Modeltype,Name), %....other code here that could recursively find all matching instances of modelFact...
My desired behavior is that when findModelName(ModelType,Name) searches the database of modelFacts, that when ModelType = friendlymodel, calling findModelName(ModelType,Name) will return "Name1", when ModelType = enemymodel, calling findModelName(ModelType,Name) will return "Name2", but when ModelType = model, calling findModelName(ModelType,Name)will return both "Name1" and "Name2". In other words, model can unify with either friendlymodel or enemymodel.

Can this be done? Or is there another approach (other than using if/elseif or or operators) that can achieve this?
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Re: Unifying domains?

Unread post by Harrison Pratt »

If you want to avoid using or then this construct works.

Code: Select all

class predicates     findModelName2 : (mydomain M, string N [out]) determ. clauses     findModelName2(model, N) :-         modelFact(_, N),         !.     findModelName2(M, N) :-         modelFact(M, N),         !.
dcgreenwood
Posts: 24
Joined: 16 Jan 2021 1:33

Re: Unifying domains?

Unread post by dcgreenwood »

Thanks but I should have been clearer. The domain would also have other values, so for example

Code: Select all

domains         mydomain : model ; enemymodel ; friendlymodel ; marker; keyposition.        
I want it to treat enemymodel = model and friendlymodel = model but friendlymodel is not equal to enemymodel or to any of the other domain elements. Basically model, enemymodel and friendlymodel are a hierarchical relationship.

There would be other such relationships, which is why I would rather not use a lot of or or if/elseif.

Like I said, I doubt it is possible, but thought I would ask. It would simplify things because it would allow matching and backtracking to find the needed facts without a chain of if/elseif
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Re: Unifying domains?

Unread post by Thomas Linder Puls »

Yes, the answer is "No".

I took a while to write this mail, so the descriptions matches your first setup (but I still think it is relevant).

I fear your are heading in a bad direction overall.

The problem is that I believe that a model (being friendly or enemy) has a lot of other properties (i.e. than name and kind). And I wonder where you would place these properties.

You could of course extend the modelFact with all these additional properties. But I fear that you would soon find that that fact will have a huge number of arguments.

Furthermore, you may then look up in that fact all the time to obtain a property of a model (and even worse if you need to modify a property).

If I am right in these assumption then I will suggest that you use object to represent models.

Code: Select all

interface model   domains     modelKind = anyKind; friendlyKind; enemyKind.   property     name : string (o).     kind : modelKind (o).   ...   end interface model
With this approach each model is an object and all properties of the model is available on that object.

You will also need a class and implementation for models.

I have added anyKind to the domain, to be able to "pose the query" you have. But it is a bit tricky, because anyKind is not actually a kind that a model can have, and I believe/fear that will in the end cause you more pain than relief.

Anyways, given that value I would define the "hierarchy" predicate:

Code: Select all

class predicates     isA_kind : (modelKind SubKind, model SuperKind). clauses     isA_kind(Kind, Kind) :-         !.     isA_kind(friendlyKind, anyKind).     isA_kind(enemyKind, anyKind).
This will be the basis of you hierarchy handling.

Here I have assumed that friendly and enemy models have same "structure" and the friendly/enemy is just a simple property of a model.

But if there is more to it, you may need two additional interfaces (and classes):

Code: Select all

interface friendlyModel supports model     % things special to friendly models end interface friendlyModel   interface enemyModel supports model     % things special to enemy models end interface enemyModel
Now that we have a way to deal with each model, we also need a way to "store" all the models we have. We could have them in a fact like you have:

Code: Select all

class facts     model_fact : (model Model).
The main query predicate would then return a model:

Code: Select all

class predicates     getModel_nd : (modelKind ModelKind) -> model Model nondeterm. clauses     getModel_nd(ModelKind) = Model :-         model_fact(Model),         isA_kind(Model:kind, ModelKind).
However, you could also consider whether there is a natural "key" with which to efficiently retrieve a model. If for example the names of models must be unique (i.e. that two models cannot have the same name regardless of for example the model kind). In that case I would store the models in a map, mapping the name to the model.

Code: Select all

class facts     modelMap : mapM{string Name, model Model} := mapM_redBlack::new() [constant].
With this it is impossible to store two models with same name. Hames here a case sensitive, so you can store "aModel" and "AModel", if you don't want to distinguish between different casing, you can use this instead:

Code: Select all

class facts     modelMap : mapM{string Name, model Model} :=         mapM_redBlack::newCustom(string::compareCaseNeutral) [constant].
With a map your query predicate will look like this:

Code: Select all

class predicates     getModel_nd : (modelKind ModelKind) -> model Model nondeterm. clauses     getModel_nd(ModelKind) = Model :-         Model = modelMap:getValue_nd(),         isA_kind(Model:kind, ModelKind).
This predicate will not be more efficient than the one with facts, but retrieving a mode from its name will:

Code: Select all

class predicates     tryGetModel  : (string Name) -> model Model determ. clauses     tryGetModel(Name) = modelMap:tryGet(Name).
Finally a little remark about the extended mydomain the additional names suggests that you want to classify anything in your program in a single hierarchy. Whether that is a good idea depends on whether these things does all have something in common and whether there are contexts in which any of these would be useful (i.e. is there any context in which a friendlyModel and a keyPosition can both make sense).
Regards Thomas Linder Puls
PDC
dcgreenwood
Posts: 24
Joined: 16 Jan 2021 1:33

Re: Unifying domains?

Unread post by dcgreenwood »

Thank you for the long response. This is, in a sense, what I was looking for...a different approach to tackle what I am doing, using something I haven't learned yet. I do indeed have facts with many arguments. I did realize last night I could add one more - "friendly, neutral, unfriendly" in addition to "model" but that just makes the facts larger.

Your description of your fear is, indeed, exactly what I have been doing throughout my project:
The problem is that I believe that a model (being friendly or enemy) has a lot of other properties (i.e. than name and kind). And I wonder where you would place these properties.

You could of course extend the modelFact with all these additional properties. But I fear that you would soon find that that fact will have a huge number of arguments.

Furthermore, you may then look up in that fact all the time to obtain a property of a model (and even worse if you need to modify a property).
I still have not been very object oriented - I have 4 separate packages, each with a lot of code in the *.pro file. I haven't progressed to learning to break it down further into many self contained objects. Which also means that all the predicates and facts have reached a level of complexity that I find myself constantly having to remind myself how certain groups of predicates work as I add new functionality. And bugs sometimes require me to completely rewrite a section, because I can't figure out what is going wrong and end up just "starting from scratch".

I'll have to study this a bit to see if is how I want to deal with these complex facts, but I appreciate you taking the time to show me the problem from a different angle. I haven done ANYTHING with properties before now. Something new to explore and understand :-)

Even if I don't do it for this fact, it reminds me that I need to be more OO and break things down further. It may help me take my code to a new level :-).

Thank you!
Post Reply