Constraint with if
Hey there,
I am trying to use a constraint, to restrict the scope of a reference. The goal is to have the reference target a concept from concept-pool A if it is the first of many references, and if not, it is to target a concept from pool B.
The problem I am having is that this approach only works when I set the references. Once they are set, MPS tells me for each reference, that it is "out of scope".
I hope you can shed some light on this...are constraints the right approach here, or am I to use actions to realize this behaviour?
Thx a lot,
Mac
Please sign in to leave a comment.
Hi Mac,
I am not 100% certain if I fully understand your issue. Maybe you can share the MPS project or, even better, a SSCCE so that we can see how you approached the issue thus far.
What I think you are trying to do is to constrain the items of the substitution menu to only show items of concept-pool A if we are in the context of the first reference (of a set of references), and only show items of concept-pool B if we are in the context of the subsequent references.
If I am not mistaken with this assumption, I think the new transformation menu language should allow you to implement that. Notice that you would also need a constraint (probably the one you already have) to validate the actual model is correct, too. But constraining the substitution menu can be achieved with the transformation language. If you are not yet using MPS 3.4, substitution actions would be one way to go.
I have too little experience with reference scopes, but I feel like this should also be an option for you.
best,
Robert
Hi Robert,
thanks for your answer, you pretty much understood what I am going for, but let me try to describe it a little more in detail.
I have Concept "Car", that contains [0..n] concepts of Concept "Carpart" as children.
Furthermore I have Concept "Truck", that contains [0..n] concepts of Concept "TruckpartReference" as children. Concept "Truckpartreference" only contains [0..1] references to Concept "Carpart".
What I am aiming for is being able to instantiate a "Car", consisting of "carparts" - and then instantiating a "Truck", that is built from the same "carparts" of the "Car", I instantiated earlier. So I would like to populate the completion menu for the target of "Truckpartreference" with the "Carparts" used in "Car".
I hope you can follow..
This seems to me as a pretty much straightforward problem, but my context-menu remains empty, and I have tried quite a few approaches so far.
Mac
Ok, I solved the referencing problem, now I can reference the correct parts.
Btw, I didn't need the transformation menu language, all I had to do is correctly implement the constraint.
The only problem remaining is the order...as stated before, my constraint contains an if. With this "if" I check for a property in the target concept for the reference, and if the property is set, it may be targeted by the first instance of the reference, but it may not be targeted by any of the following references.
Functionalitywise everything works, meaning I can choose the concepts with the property in question as targets for the first, but not for the following instances of the reference, but sadly once the first reference is set and I press enter to set a second reference, the target of the first reference is underlined in red and noted "reference A is out of scope" --- it seems, the first reference is reevaluated.
Is there any way to influence this?
Hmmm... is it possible for you to share your project here as an attachement?
Are you working with MPS 3.4.1? We do have a "reference is out of scope" issue as well ever since we migrated to 3.4.1 which seems to be a MPS issue.
Anyway, I think I understand what you try to achieve as well as your description of the problem. Without seeing your constraint and especially the if-statement, however, it is hard to suggest changes.
Unfortunately I cannot share my Project here, but I think I can cut down the Problem a bit like this:
Suppose my constraint looks like this:
concepts constraints NodeRef{
can be child <none>
can be parent <none>
can be ancestor <none>
<<property constraints>>
link {Instance}
referent set handler:<none>
scope:
(exists, referenceNode, contextNode, containingLink, linkTarget, operationContext, enclosingNode, model, position, contextRole)->Scope {
if (contextNode.ancestor<concept = NodeContainer>.NodeRefs.first.Instance == null){
return ListScope.forNamedElements(contextNode.ancestor<concept = NodeContainer>.NodeMapping.NodePairs.where({~it => it.isStartingNode == true}));
}else {
return ListScope.forNamedElements(contextNode.ancestor<concept = NodeContainer>.NodeMapping.NodePairs.where({~it => it.isStartingNode == false}));
...meaning something like: If you have not assigned a Node in the Nodecontainer, show all the Nodes marked as "StartingNodes" in the context menu. Otherwise show all the other nodes, which seems like a sensible thing to do from my point of view.
However, as soon as I assign a starting node to the first nodeinstance, this first node now has an Instance (i.e. a reference), and therefore the constraint does not allow the setting of a starting node any longer and marks it as an error. And that is exactly my problem. I don't see a way to handle these kind of case-distinctions in a constraint, but an action or a behavior wouldn't give me the kind of usability I am looking for, as I see it.
I must be missing something...
Oh sorry, and yes, I am using MPS 3.4.1, although I experienced a similar problem in 3.3, which I was using before.
Hi Mac,
sorry for the delay, pretty busy lately.
I see. What is happening is that you define a valid scope based on a condition (you already know that, of course, it is just for my own recollection). Now, the scope you define is valid for ALL references.
This means that, once you add a first reference to your model, your scope gets minimized so that it doesn't allow those that have the flag "isStartingNode" set. Again, this minimized scope is valid for all the references, including the first one, and since this reference's flag "isStartingNode" has the value "true", it becomes "out of scope".
If you want to solve this using scopes, I think you would need to distinguish between the first reference and the following references in your structure aspect, i.e. distringuish between them on a concept-level, so that you could define two different scopes (the one of your if-clause for the starting nodes, the one of the else-clause for the rest)
I really don't see how you could achieve what you want with scopes alone. Again, a more flexible way to do it is to use substitution actions, or the new transformation menu language, that is. This gives you the capability to specify the content of the substitution menu "locally", meaning based on the specific context you are in (in your case, whether or not you invoke the substitution menu for the head reference, or the tail).
Compare this again to scopes, which you define for the reference (in your case "Instance"), and it is evaluated for all instances of the Instance reference (not context sensitive, if you will).
Hope this helps,
Robert