Open eclipse-qvt-oml-bot opened 1 week ago
By Ed Willink on Oct 14, 2013 05:14
This should generate a better error message.
QVT predates the conformance of Collection to OclAny in OCL 2.2, so a Collection cannot be constructed as a QVT object. The conformance of Collection to OclAny leads to specification inconsistencies without significant benefits; it might be retracted/revised on OCL 2.5.
A Collection should be constructed as "OrderedSet{'abc'}".
QVT action: improve the semantic error message.
By Christopher Gerking on Oct 14, 2013 15:04
(In reply to Ed Willink from comment #1)
QVT action: improve the semantic error message.
The problem is that there is actually no error message at all.
By Sergey Boyko on Oct 14, 2013 17:00
(In reply to Christopher Gerking from comment #2)
(In reply to Ed Willink from comment #1)
QVT action: improve the semantic error message.
The problem is that there is actually no error message at all.
I would like to point that actually QVTo allows to create objects of collection types (though implicitly):
mapping String::mapToSequence() : Sequence(String) {\ end {\ assert fatal (result->isEmpty());\ }\ }
QVT specification in section "8.2.1.24 ObjectExp" does not prohibit creation of such objects.
I see two options:
a) prohibits explicit 'object' expression for collection types
b) improves evaluation of 'object OrderedSet(String){"abc"}' so that properly populated collection will be created. Note that currently created collection has correct type. Btw, that is why no error message is displayed in this case.
To me option b) is more correct and follows the direction of QVTo. However option a) is easier to implement.
By Ed Willink on Oct 14, 2013 17:22
If you implement "object Collection..." you have a contradiction.
"object "... creates distinct objects so that
object X {...} <> object X {...}
but for Collections and Tuples
X { ... } = X { ... }
so if you make "object Set{...} a synonym for Set{...} you would need to find a way to create non-equal equal 'objects'.
So
"object T{...}" where T is a DataType is a semantic error.
By Sergey Boyko on Oct 15, 2013 02:53
(In reply to Ed Willink from comment #4)
If you implement "object Collection..." you have a contradiction.
"object "... creates distinct objects so that
object X {...} <> object X {...}
but for Collections and Tuples
X { ... } = X { ... }
Can you please specify which section in specifications declares such equation.
May be I did not understand your point correctly but for example in QVTo this is not so. For declarations:
var t1 := Sequence{"1"};\ var t2 := Sequence{"1"};
't1' is not 't2'. These objects are distinct (have different addresses). Of course comparision 't1 = t2' returns 'true' due to implementation of 'equal()' method in org.eclipse.ocl.util.ObjectUtil class.
This is like in Java - while String objects are generally interned it's a rule of thumb to use method '.equals()' in place of '==' to compare strings.\ For example String can be initialized like:\ String s = new String("hello");\ and\ String s = "hello";
so if you make "object Set{...} a synonym for Set{...} you would need to find a way to create non-equal equal 'objects'.
So
"object T{...}" where T is a DataType is a semantic error.
By Ed Willink on Oct 15, 2013 07:40
(In reply to Sergey Boyko from comment #5)
Can you please specify which section in specifications declares such equation.
The semantics of OCL equality has been a gaping hole for a long time; OCL 2.3 actually specifies comparison of objects, so true need not be equal to true! OCL 2.4 provides increased clarity specifying that DataTypes are compared by deep value.
May be I did not understand your point correctly but for example in QVTo this is not so. For declarations:
var t1 := Sequence{"1"}; var t2 := Sequence{"1"};
't1' is not 't2'. These objects are distinct (have different addresses). Of course comparision 't1 = t2' returns 'true' due to implementation of 'equal()' method in org.eclipse.ocl.util.ObjectUtil class.
Yes. Distinct CollectionLiteralExp's may be equal. But for QVTo an ObjectExp: An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class.
NB class. A Collection is not a class. Class instances always compare not equal. Collection instances can compare equal, because they are DataTypes.
So you would have to change the QVTo spec to allow 1) an ObjectExp to create a Collection 2) to allow the extent to be unnamed\ Since there is already a perfectly good AS node that does this, I don't see any likelihood that this would be permitted at the AS level and doing so as syntax sugar at the CS level seems daft too. More likely, the introduction of type constructors in OCL will make some forjms of the ObjectExp syntax redundant.
So why was there no error message from the missing extent name in:
object OrderedSet(String){"abc"}
By Sergey Boyko on Oct 15, 2013 12:18
(In reply to Ed Willink from comment #6)
(In reply to Sergey Boyko from comment #5)
May be I did not understand your point correctly but for example in QVTo this is not so. For declarations:
var t1 := Sequence{"1"}; var t2 := Sequence{"1"};
't1' is not 't2'. These objects are distinct (have different addresses). Of course comparision 't1 = t2' returns 'true' due to implementation of 'equal()' method in org.eclipse.ocl.util.ObjectUtil class.
Yes. Distinct CollectionLiteralExp's may be equal. But for QVTo an ObjectExp: An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class.
NB class. A Collection is not a class. Class instances always compare not equal. Collection instances can compare equal, because they are DataTypes.
It's not so for Class instances. \ For example create new Ecore model (say 'My.ecore'). There create package with class MyClass which in turn contains property 'name : String'. Generate model code for it. In generated source file 'MyClasssImpl.java' override the method 'equals()' as shown below:
@Override\ public boolean equals(Object obj) {\ if (false == obj instanceof MyClasssImpl) {\ return false;\ }\ MyClasssImpl another = (MyClasssImpl) obj;\ if (name == null) {\ return null == another.name;\ }\ return name.equals(another.name);\ }
With this metamodel try the following script:
var t1 := object MyClasss{name:="a"};\ var t2 := object MyClasss{name:="a"};\ assert fatal (t1 = t2);
However I realized that ObjectExp requires Class as the type of the object to be created.\ This immediately leads to the fact that the following mapping is not correct:
mapping String::mapToSequence() : Sequence(String) {\ end {\ assert fatal (result->isEmpty());\ }\ }
also the following:
mapping String::mapToList() : List(String) {\ result->append("a")\ end {\ assert fatal (result->size() = 1);\ }\ }
Is this what you meant?
So you would have to change the QVTo spec to allow 1) an ObjectExp to create a Collection 2) to allow the extent to be unnamed
Already modeled in QVTo with a notion of 'unbound' model extent.
Since there is already a perfectly good AS node that does this, I don't see any likelihood that this would be permitted at the AS level and doing so as syntax sugar at the CS level seems daft too. More likely, the introduction of type constructors in OCL will make some forjms of the ObjectExp syntax redundant.
So why was there no error message from the missing extent name in:
object OrderedSet(String){"abc"}
For the transformation QVTo engine supports 'unbound' extent. It is used for example in case when transformation doesn't declare any 'inout' nor 'out' model parameters but needs to create objects:
modeltype Ecore uses ecore('http://www.eclipse.org/emf/2002/Ecore');\ transformation bug417751;\ main() {\ var t := object EClass{}; ...\ }
By Ed Willink on Oct 15, 2013 14:17
'unbound' extent seems like a really horrible facility.
Overriding equals is very very strongly discouraged for EObjects, so basically it is illegal.
My point is that OCL 'defines' that
a1 = a2
is true for class instances if a1 and a2 are the same object (same address)\ is false for class instances if a1 and a2 are different objects (different address)\ is true for datatype instances if a1 and a2 have the same deep value\ is false for datatype instances if a1 and a2 do not have the same deep value
If the QVTo specification contradicts this, then the specification is likely to change to align with OCL.
It seems clear that supporting "object Set" would lead to a contradiction and so it will become invalid once the QVT specification is clarified. IMHO the current words "ObjectExp: An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class" can be taken to mean that it is illegal today.
By Sergey Boyko on Oct 17, 2013 03:26
(In reply to Ed Willink from comment #8)
Overriding equals is very very strongly discouraged for EObjects, so basically it is illegal.
My point is that OCL 'defines' that
a1 = a2
is true for class instances if a1 and a2 are the same object (same address) is false for class instances if a1 and a2 are different objects (different address) is true for datatype instances if a1 and a2 have the same deep value is false for datatype instances if a1 and a2 do not have the same deep value
If the QVTo specification contradicts this, then the specification is likely to change to align with OCL.
It seems clear that supporting "object Set" would lead to a contradiction and so it will become invalid once the QVT specification is clarified. IMHO the current words "ObjectExp: An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class" can be taken to mean that it is illegal today.
MOF 2.0 specification section "9.1 Element" defines equality operator as below:\ '\ equals(object: Object): Boolean
Determines if the object equals this Element instance. For instances of Class, returns true if the element and this Element instance are references to the same Object. For instances of DataType, returns true if the object has the same value as this Element instance. Returns false for all other cases.\ '
MOF states that DataType metaclass is not abstract thus instances of DataType can be created (similar to EDataType in EMF).\ Essence of Data Type object is not that it cannot be instantiated but that instances of DataType are compared by value.
This is especially matters for List type which is also collection type. It's clear that each occurrence of collection literal expression refers to distinct instance of List.
There is no reason to prohibit creation of DataType instances (by means of object expression) as long as they satisfy the equality relation.
By Sergey Boyko on Oct 17, 2013 03:41
QVTo implementation fully comply with these equality relations:
1.\ var t1 := Sequence{"1"};\ var t2 := Sequence{"1"};\ var s := Set{t1, t2};\ assert fatal (t1 = t2);\ assert fatal (s->size() = 1);
3.\ var t1 := "a".map mapToList();\ var t2 := "a".map mapToList();\ var s := Set{t1, t2};\ assert fatal (t1 = t2);\ assert fatal (s->size() = 1);
mapping String::mapToList() : List(String) {\
result->append(self)\
}
4.\ var t1 := "a".map mapToSequence();\ var t2 := "a".map mapToSequence();\ var s := Set{t1, t2};\ assert fatal (t1 = t2);\ assert fatal (s->size() = 1);
mapping String::mapToSequence() : Sequence(String) {\
}
5.\ var t1 := "a".map mapToSequence();\ var t2 := "a".map mapToSequence();\ var s := Set{t1, t2};\ assert fatal (t1 = t2);\ assert fatal (s->size() = 1);
mapping String::mapToSequence() : Sequence(String) {\
init {\
result := self->asSequence();\
}\
}
6.\ var t1 := "a".map mapToSequence();\ var t2 := Sequence{"a"};\ var s := Set{t1, t2};\ assert fatal (t1 = t2);\ assert fatal (s->size() = 1);
mapping String::mapToSequence() : Sequence(String) {\
init {\
result := self->asSequence();\
}\
}
7.\ var t1 := object Sequence(String){};\ var t2 : Sequence(String) := Sequence{};\ var s := Set{t1, t2};\ assert fatal (t1 = t2);\ assert fatal (s->size() = 1);
QVTo behavior is aligned with MOF and OCL and does not contradict with them.\ The problem is clearly in incomplete population of the collection types.
By Ed Willink on Oct 17, 2013 05:43
-1 to supporting Collection construction using object/ObjectExp.
(In reply to Ed Willink from comment #8)
'unbound' extent seems like a really horrible facility.
I cannot find any reference to this in the spec; certainly not using the 'unbound' word.
(In reply to Sergey Boyko from comment #9)
MOF 2.0 specification section "9.1 Element" defines equality operator as below: ' equals(object: Object): Boolean
I presume you mean MOF::Reflection::equals(object : MOF::Reflection::Object). But MOF::Reflection is specifically excluded from Essential OCL (13.2 point 7). So this operation and argument do not exist in OCL and so presumably not in QVTo.
Even if MOF::Reflection was merged, there is no indication that Element::equals has any relationship to OCL's "="; just the same as Java's Object::equals() is different to "=". (An evolution of OCL to merge MOF::Reflection is very unmlikely since it contains non-query methods; more likely facilities such as getMetaClass() and container() will be cherry picked with new names such as oclType() and oclContainer(). MOF::Reflection::equals might even appear as oclEquals() but it won't be the same as "=".
(In reply to Sergey Boyko from comment #10)
var t1 := object Sequence(String){"abc"}; var t2 := object Sequence(String){"abc"}; var s := Set{t1, t2}; assert fatal (t1 = t2); assert fatal (s->size() = 1);
This is the contradiction.
objects are class instances (ObjectExp currently specify this). Class instances are distinct under "=".
So this violates the specification in three ways:
a) 8.2.1.24 "An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class"
Sequence(String) is not a Class
b) The body of an ObjectExp is an expression_block, which must have side effects to populate the properties of the instantiation. I see no description justifying gathering up all the unassigned partial expressions e.g. "abc" and using them to perform a positional assignment.
c) 8.2.1.24 "a new object of the given class is created" \ assert fatal (t1 = t2) demonstrates that new objects were not created by each ObjectExp. The two new objects compare equal under circumstances where no version of the OCL specification has permitted equality.
The specification violations associated with a) and b) could be removed if the QVTo specification evolved.
However the problem of c) is fundamental. The specification is sensible and unambiguous on this point. It would be even more unambiguous if 8.2.1.24 referred to "Class" rather than "class".
Therefore object Sequence(String){"abc"} must be a syntax/semantic error.
By Christopher Gerking on Oct 17, 2013 08:38
(In reply to Ed Willink from comment #11)
a) 8.2.1.24 "An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class"
I wonder why we take the specification at face value in this particular case, although it is known to have deficiencies. Is there a good reason for the above restriction to "class" objects? Otherwise, why not relaxing the spec here?
In general I'm fine with a semantic error for this case. However, this will invalidate lots of circulating QVTo scripts. Personally, I used "object OrderedSet(...)" quite heavily in the past, because I didn't know that the "object" keyword is omissible. We should at least consider to retain our support as a legacy, maybe coming up with a warning instead of an error.
By Ed Willink on Oct 17, 2013 09:18
(In reply to Christopher Gerking from comment #12)
I wonder why we take the specification at face value in this particular case, although it is known to have deficiencies. Is there a good reason for the above restriction to "class" objects? Otherwise, why not relaxing the spec here?
In comment #10, I observed that the specification could evolve to make the a) is-a-class, b) property assignment violations go away. It cannot sensibly evolve to make the separate objects contradiction go away.
Legacy support with a warning is good.
By Christopher Gerking on Oct 17, 2013 10:09
(In reply to Ed Willink from comment #13)
In comment #10, I observed that the specification could evolve to make the a) is-a-class, b) property assignment violations go away. It cannot sensibly evolve to make the separate objects contradiction go away.
I still have problems to see the contradiction.
(In reply to Ed Willink from comment #13)
assert fatal (t1 = t2) demonstrates that new objects were not created by each ObjectExp.
Why? In my understanding it only demonstrates that these objects are structurally equal. Why does this imply that no "new" objects were created?
By Ed Willink on Oct 17, 2013 10:27
(In reply to Christopher Gerking from comment #14)\ Because OCL specifies that distinct objects are not equal. (Remarkably the same as Java.)
In Java (new XXX() = new XXX()) is necessarily false. This is all that OCL is saying, and what QVTo is contradicting.
By Christopher Gerking on Oct 17, 2013 11:26
(In reply to Ed Willink from comment #15)
(In reply to Christopher Gerking from comment #14) Because OCL specifies that distinct objects are not equal.
What is your definition of an object? If we would relax the QVTo spec such that object expressions may also refer to datatypes, then distinct "objects" could be equal.
By Ed Willink on Oct 17, 2013 11:40
Yes. Object can be defined in all sorts of ways, and it is certainly possible to juggle the words to make the QVTo legacy bug only lame, but it requires at least three hard-to-justify changes to the QVTo specification; it isn't going to happen.
"object" clearly implies "construct" and so different.
By Adolfo Sanchez-Barbudo Herrera on Oct 17, 2013 14:20
Hi All,
I quite simpathize with Ed´s point of view.
Firstly, I don´t see the need to use object expressions to initialize any kind of collection: We already have an specic collection literal expression concept for that.
Secondly, abstract syntax clearly states that the instantiatedClass (from InstantiationExp) is a Class, and collections are DataTypes.
Thirdly, how an String literal expression, like in this case, must be interpreted when populating objects ?. An specific logic when dealing with data types ?.
In conclusion, I don´t see the point nor the need to change the specification to support that object Sequence(String) { "ababab" } expression.
The only point I was worried about was Sergey´s comment:
(In reply to Sergey Boyko from comment #3)
(In reply to Christopher Gerking from comment #2)
I would like to point that actually QVTo allows to create objects of collection types (though implicitly):
mapping String::mapToSequence() : Sequence(String) { end { assert fatal (result->isEmpty()); } }
But firstly, we would have an awkward use case:
mapping out MyClass::createSomeStrings() : Sequence(String) {\ "String1", "String2"\ }
Secondly, again we have to interpret the semantinc of string literal expressions inside the body of the mapping.
A proper equivalent mapping would be:
mapping out MyClass::createSomeStrings() : Sequence(String) {\ population {\ result := Sequence(String) { "String1", "String2" };\ }\ }
or if dealing with lists:
mapping out MyClass::createSomeStrings() : List(String) {\ population {\ result.add("String1");\ result.add("String2");\ }\ }
In my opinion, actions to take:
"The rule for interpreting a body in which there is no population keyword is as follows:
also consider the collection types, which in somehow is like having multiple results (an undetermined number of result objects).
One last point. I´m wondering how the tracing mechanism is working behind the scenes. Is the input object traced with a collection ?, or is it traced with every object which belong to the collection ?.
Cheers,\ Adolfo.
By Ed Willink on Oct 17, 2013 15:03
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #18)
(In reply to Sergey Boyko from comment #3)
(In reply to Christopher Gerking from comment #2) But firstly, we would have an awkward use case:
mapping out MyClass::createSomeStrings() : Sequence(String) { "String1", "String2" }
I can't see how this is valid. It is much more like a query or helper.
Surely a Mapping needs to mention result somewhere?
A proper equivalent mapping would be:
mapping out MyClass::createSomeStrings() : Sequence(String) { population { result := Sequence(String) { "String1", "String2" }; } }
Indeed. But since the problem QVTo legacy syntaxes seem to be without foundation in the QVTo spec, I see no need to raise an OMG issue. The spec does not have to explicitly denounce every implementation misunderstanding.
By Adolfo Sanchez-Barbudo Herrera on Oct 17, 2013 16:38
(In reply to Ed Willink from comment #19)
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #18)
(In reply to Sergey Boyko from comment #3)
(In reply to Christopher Gerking from comment #2) But firstly, we would have an awkward use case:
mapping out MyClass::createSomeStrings() : Sequence(String) { "String1", "String2" }
I can't see how this is valid. It is much more like a query or helper.
Surely a Mapping needs to mention result somewhere?
A proper equivalent mapping would be:
mapping out MyClass::createSomeStrings() : Sequence(String) { population { result := Sequence(String) { "String1", "String2" }; } }
Indeed. But since the problem QVTo legacy syntaxes seem to be without foundation in the QVTo spec, I see no need to raise an OMG issue. The spec does not have to explicitly denounce every implementation misunderstanding.
Apologies, I should have used a semicolon (to seperate several expressions in the body´s expression block). In the end, is a similar example to what is being proposed above. If we used the original example, the mapping could look like as follows:
mapping out MyClass::createSomeStrings() : OrderedSet(String) {\ "abc"\ }
From the specification piece I mentioned before:
"The rule for interpreting a body in which there is no population keyword is as follows:
The mapping above would be equivalent to
mapping out MyClass::createSomeStrings() : OrderedSet(String) {\ population {\ result := object OrderedeSet(String) { "abc" };\ }\ }
because " : OrderedSet(String)" is arguably the "unique result" of the mapping.
I think this the issue which Sergey referred at comment 3, which I think needs to be clarified in the specification.
Regards,\ Adolfo.
By Ed Willink on Oct 17, 2013 17:27
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #20)
mapping out MyClass::createSomeStrings() : OrderedSet(String) { "abc" }
It doesn't seem to make any difference. This is nothing like a mapping.
because " : OrderedSet(String)" is arguably the "unique result" of the mapping.
This seems to be a complete ficyion of an Eclipse QVTo implementation.
I think this the issue which Sergey referred at comment 3, which I think needs to be clarified in the specification.
I see no reason why OMG QVT should take any notice of this unfortunate accident.
I certainly see no reason for OMG QVT to introduce such a klunky troublesome inconsistent redundant syntax. I won't be promoting the idea at the OMG, and will argue strongly against it if anyone else does.
By Adolfo Sanchez-Barbudo Herrera on Oct 18, 2013 05:56
(In reply to Ed Willink from comment #21)\
I see no reason why OMG QVT should take any notice of this unfortunate accident.
I certainly see no reason for OMG QVT to introduce such a klunky troublesome inconsistent redundant syntax. I won't be promoting the idea at the OMG, and will argue strongly against it if anyone else does.
Hi Ed,
I think there has been a misunderstanding. As I firstly commented I'm against introducing this "inconsistent redundant syntax".
The problem with Sergey's example arises with the current OMG spec:
mapping String::mapToSequence() : Sequence(String) {\ end {\ assert fatal (result->isEmpty());\ }\ }
is equivalent to write:
mapping String::mapToSequence() : Sequence(String) {\ population {\ object result : Sequence(String) {} \ }\ end {\ assert fatal (result->isEmpty());\ }\ }
I'm not in favour of having the second syntax be accepted, just noticing the problem.
Problem: Unique result + no population section = implicit object expression. So we have a problem having collections as the "unique result" of a mapping which doesn't include a population keyword as in the Sergey's example.
On the other hand, and perhaps that's what you mean with the "fiction of Eclipse QVTo implementantion", those mappings (returning collections, without population keyword) should not be allowed. A sort of error message should be thrown in the Sergey's example.
In any case, it should not be harmful adding a note in the specification what this respects, to clarify what a "unique result" is.
a) mapping MyClass::mapping1() : MyClass2 { ...\ }
b) mapping MyClass::mappign2() : c1 : MyClass2, c2: MyClass3 { ...\ }
c). mapping Myclass::mapping3() : Sequence(MyClass2) { ...\ }
a) is clearly a mapping unique result\ b) is clearly a mapping multiple result\ c) could arguably be a "unique result", since just one collection is returnead even though multiple objects may belong to the collection.
I think that just a note about this particular issue could suffice. Actually, it should be generalized to data types (the implicit object expression affects to every type which is not a class, although I quite doubt that a mapping are typically defined with datatypes different to collections, you usually map to true classes).
To the paragraph:
"1. If the mapping operation defines a unique result, the list of expressions in the body is the list of expressions of the -\ unique - implicit object expression (see ObjectExp) contained by the population section."
add:
"Note that if a mapping returns a unique data type (e.g. a collection type), the population keyword is mandatory because the implicit object expression can only be applied to classes."
A last minute thought. I think if we should rather rationalize about if it's meant to be "unique" or "single", which have subtle differences in English. Giving that point 2 says "more than one result", I think that "single" could be more appropriate.
Therefor, the issue resolution could be:
Replace the paragraph:
"1. If the mapping operation defines a unique result, the list of expressions in the body is the list of expressions of the -\ unique - implicit object expression (see ObjectExp) contained by the population section."
by
"1. If the mapping operation defines a single result, the list of expressions in the body is the list of expressions of the -\ single- implicit object expression (see ObjectExp) contained by the population section. Note that if a mapping returns a single data type (e.g. a collection type), the population keyword is mandatory because the implicit object expression can only be applied to classes."
and
Replace the paragraph:
"This notation convention facilitates the writing of concise specifications since the situation where there is a unique result is very common."
by
"This notation convention facilitates the writing of concise specifications since the situation where there is a single result is very common."
Regards,\ Adolfo.
P.S: I still have doubts about traces produced by mappings returning collections. Sergey do you control this part of the implementation ? Any comments ?
By Christopher Gerking on Oct 18, 2013 06:37
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #22)
On the other hand, and perhaps that's what you mean with the "fiction of Eclipse QVTo implementantion", those mappings (returning collections, without population keyword) should not be allowed. A sort of error message should be thrown in the Sergey's example.
I'm strongly against this, because it drops the existing support for "multi mappings". You can't just convert such a mapping into a helper or something, because you lose features like
I use multi mappings like in Sergey's example quite heavily, so I hope there's a solution to not drop the support.
I still have doubts about traces produced by mappings returning collections. Sergey do you control this part of the implementation ? Any comments ?
See above. Traceability links are actually recorded for mapped collections, and my existing code relies on them quite often.
By Adolfo Sanchez-Barbudo Herrera on Oct 18, 2013 06:52
(In reply to Christopher Gerking from comment #23)
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #22)
On the other hand, and perhaps that's what you mean with the "fiction of Eclipse QVTo implementantion", those mappings (returning collections, without population keyword) should not be allowed. A sort of error message should be thrown in the Sergey's example.
I'm strongly against this, because it drops the existing support for "multi mappings".
Hi Christopher,
You have missed the point:
"Unique result + no population section = implicit object expression."
"Multi mappings" can't be prohibited as you say, I'm just saying that the population keyword needs to be mandatory in those mapping returning collections:
mapping String::mapToSequence() : Sequence(String) {\ population {\ // populate the result sequence with some Strings\ } \ }
If you miss the population keyword, the editor must show an error. When including the population keyword, you were preventing the implicit object expression creation which can't be used with collections.
See above. Traceability links are actually recorded for mapped collections, and my existing code relies on them quite often.
But, what is being traced ?\ a) The collection ? (1-1 trace)\ b) The objects belonging to the collection ? (1-N trace)\ c) Anything else more complicated ?
Anyway, this point is quite off-topic (irrelevant to this issue resolution), just wondering about if "unique (single) collection result" produces a 1-1 object trace, or a 1-N one.
Cheers,\ Adolfo.
By Christopher Gerking on Oct 18, 2013 07:14
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #24)
"Multi mappings" can't be prohibited as you say, I'm just saying that the population keyword needs to be mandatory in those mapping returning collections
Seems as if I didn't understand the role of the population keyword up to now. I though it was just optional to mark the population section. According to the specification, it has more meaning than I expected.
Anyway, I have no problems with adding the population keyword to my existing code. Just thought about the need for a legacy support in Eclipse QVTo.
But, what is being traced ? a) The collection ? (1-1 trace) b) The objects belonging to the collection ? (1-N trace) c) Anything else more complicated ?
Anyway, this point is quite off-topic (irrelevant to this issue resolution), just wondering about if "unique (single) collection result" produces a 1-1 object trace, or a 1-N one.
I produces a 1-1 trace record, i.e.
context object <-> result collection.
Obviously, such a result collection can not be persisted correctly, because it is not a concrete target model element. However, persistence is another issue. Inside the executing transformation, the trace access works just fine.
By Ed Willink on Oct 18, 2013 08:46
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #22)
Problem: Unique result + no population section = implicit object expression. So we have a problem having collections as the "unique result" of a mapping which doesn't include a population keyword as in the Sergey's example.
The OMG specification could certainly be much clearer in terms of how mapping short forms expand into their long forms. ... Continued as Bug 419840.
By Sergey Boyko on Oct 18, 2013 11:16
(In reply to Ed Willink from comment #11)
(In reply to Sergey Boyko from comment #10)
var t1 := object Sequence(String){"abc"}; var t2 := object Sequence(String){"abc"}; var s := Set{t1, t2}; assert fatal (t1 = t2); assert fatal (s->size() = 1);
This is the contradiction.
objects are class instances (ObjectExp currently specify this). Class instances are distinct under "=".
So this violates the specification in three ways:
a) 8.2.1.24 "An object expression is an inline instantiation facility. It contains a constructor body, it refers to a class"
Sequence(String) is not a Class
In QVTo there are three ways to instantiate object.
a) "8.2.1.15 MappingOperation" where instantiation type is specified as follow:\ result: VarParameter [*] {composes,ordered}\ Thus type to be instantiated is specified as pure Type (where DataType and Class are successors)
b) "8.2.1.24 ObjectExp" where instantiation type is specified as follow:\ instantiatedClass: Class [0..1](from InstanciationExp)
c) "8.2.2.23 InstantiationExp" where instantiation type is specified as follow:\ instantiatedClass : Class [1]
I would say that options b) and c) just don't give clear explanation of type 'Class' which they are referred to. Actually it's any Classifier that is not abstract and thus can be instantiated.
Also take into account that QVTo allows to define metamodel locally ("8.4.6 Notation for Metamodels"). One can define 'datatype', 'primitive', 'exception' and 'class'. \ Since there is no concrete syntax on how to declare literals for datatypes their instances are necessarily should be created by means of instantiation expression.
b) The body of an ObjectExp is an expression_block, which must have side effects to populate the properties of the instantiation. I see no description justifying gathering up all the unassigned partial expressions e.g. "abc" and using them to perform a positional assignment.
Yes, it's actually the most complicated part of changes but it relates to concrete syntax only.
c) 8.2.1.24 "a new object of the given class is created" assert fatal (t1 = t2) demonstrates that new objects were not created by each ObjectExp. The two new objects compare equal under circumstances where no version of the OCL specification has permitted equality.
You wrongly understand the equality relation. Let's consider the following examples:
a)\ var t1 := Sequence{"1"};\ var t2 := Sequence{"1"};
Are 't1' and 't2' the same instances? They can be the same or can be different - it's implementation specific. And MOF\ clearly defines how they participate in equality relation - they are compared by value so equality relation does not\ depend on how these instances were allocated.\ So 't1 = t2' is 'true'.
b)\ var t1 := List{"1"};\ var t2 := List{"1"};
Are 't1' and 't2' the same instances? They are definitely different instances. And once again MOF\ clearly defines how they participate in equality relation - they are compared by value.\ So 't1 = t2' is 'true'.
c) \ var t1 := Tuple {name="a",size=1};\ var t2 := Tuple {name="a",size=1};
This variant becomes more interesting since Tuple inherits both DataType and Class. Once again MOF\ clearly defines how they participate in equality relation - they are compared by value.\ So 't1 = t2' is 'true'.
In any of the items above we can replace each literal expression onto instantiation expression and nothing will change.\ Instances 't1' and 't2' will become definitely different but according to MOF they will still be compared by value.
The specification violations associated with a) and b) could be removed if the QVTo specification evolved.
However the problem of c) is fundamental. The specification is sensible and unambiguous on this point. It would be even more unambiguous if 8.2.1.24 referred to "Class" rather than "class".
Therefore object Sequence(String){"abc"} must be a syntax/semantic error.
By Sergey Boyko on Oct 18, 2013 11:26
(In reply to Ed Willink from comment #15)
(In reply to Christopher Gerking from comment #14) Because OCL specifies that distinct objects are not equal. (Remarkably the same as Java.)
In Java (new XXX() = new XXX()) is necessarily false. This is all that OCL is saying, and what QVTo is contradicting.
You're absolutely wrong. QVT (and OCL and MOF) is not Java. You specified a rule from another domain. \ Distinct instances are compared according to their type. For instances of Class they are compared by references. For instances of DataType they are compared by value. That's it. Neither OCL not QVT gives a way to compare instances of DataTypes by reference, they always compare by value.
By Sergey Boyko on Oct 18, 2013 11:40
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #24)
(In reply to Christopher Gerking from comment #23)
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #22)
On the other hand, and perhaps that's what you mean with the "fiction of Eclipse QVTo implementantion", those mappings (returning collections, without population keyword) should not be allowed. A sort of error message should be thrown in the Sergey's example.
I'm strongly against this, because it drops the existing support for "multi mappings". Hi Christopher,
You have missed the point:
"Unique result + no population section = implicit object expression."
"Multi mappings" can't be prohibited as you say, I'm just saying that the population keyword needs to be mandatory in those mapping returning collections:
mapping String::mapToSequence() : Sequence(String) { population { // populate the result sequence with some Strings }
}If you miss the population keyword, the editor must show an error. When including the population keyword, you were preventing the implicit object expression creation which can't be used with collections.
QVTo supports such cases by means of 'init{}' section.\ Since "8.2.1.15 MappingOperation" defines that implicit instantiation occurs right after 'init{}' section then one just have to assign 'result' in it.
For example, suppose you declare mapping which results in abstract classifier:
mapping String::mapToSequence() : EClassifier {\ }
Is it valid? Yes, but in order to invoke it one should assign 'result' in init section:
mapping String::mapToSequence() : EClassifier {\ init {\ result := object EClass{}\ }\ }
The same is applied to collection. You can find a number of junit test scripts that operate this way.
By Sergey Boyko on Oct 18, 2013 11:59
(In reply to Christopher Gerking from comment #25)
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #24)
"Multi mappings" can't be prohibited as you say, I'm just saying that the population keyword needs to be mandatory in those mapping returning collections
Seems as if I didn't understand the role of the population keyword up to now. I though it was just optional to mark the population section. According to the specification, it has more meaning than I expected.
Population section defines only the means to update existing object (that should be initialized in 'init{}' or otherwise will be created right before population section). \ See "8.2.1.24 ObjectExp" : If the variable is non-null, no instantiation occurs but the constructor body is used to update the existing code.
It uses "object x:X { … } // An explicit variable here" concrete syntax. Also there are number of 'population' usage in junit test scripts.
Anyway, I have no problems with adding the population keyword to my existing code. Just thought about the need for a legacy support in Eclipse QVTo.
But, what is being traced ? a) The collection ? (1-1 trace) b) The objects belonging to the collection ? (1-N trace) c) Anything else more complicated ?
Anyway, this point is quite off-topic (irrelevant to this issue resolution), just wondering about if "unique (single) collection result" produces a 1-1 object trace, or a 1-N one.
I produces a 1-1 trace record, i.e.
context object <-> result collection.
Obviously, such a result collection can not be persisted correctly, because it is not a concrete target model element. However, persistence is another issue. Inside the executing transformation, the trace access works just fine.
Christopher, could you please describe more in details under under what conditions collections are not persisted in trace.
I used the following script:
modeltype Ecore uses ecore('http://www.eclipse.org/emf/2002/Ecore');\ transformation bug419339(in m : Ecore, out m2 : Ecore);
main() {\ var root := m.objectsOfType(EPackage)->any(true);\ root.map toSequence()->map toList();\ }\ mapping EPackage::toSequence() : Sequence(EClass) {\ init {\ result := Sequence {object EClass{}}\ }\ }\ mapping EClass::toList() : Sequence(EClass) {\ init {\ result := Sequence {object EClass{}}\ }\ }
Persisted trace attached.
By Sergey Boyko on Oct 18, 2013 12:02
Created attachment 236665 Trace file for collections
:notepad_spiral: bug419339.qvtotrace
By Adolfo Sanchez-Barbudo Herrera on Oct 18, 2013 12:19
Also take into account that QVTo allows to define metamodel locally ("8.4.6 Notation for Metamodels"). One can define 'datatype', 'primitive', 'exception' and 'class'. Since there is no concrete syntax on how to declare literals for datatypes their instances are necessarily should be created by means of instantiation expression.
This is a good point, which also applies to metamodels in general. I'm wondering how QVTo is working with metamodels dealing with custom datatypes...
In any case, I'm reluctant to relax ObjectExp for this purpose. The point I mentioned in comment 18 sounds a reason to not to do so:
"Thirdly, how an String literal expression, like in this case, must be interpreted when populating objects ?. An specific logic when dealing with data types ?."
I'd better and more accurately reword as:
If the object block expression is used to populate the created/updated object, how the evaluator should work finding an object expression whose block expression is just a StringLiteralExp ?:
object a : AType {\ "abc"\ }
I'm not fan of this, certainly.... specially when we already have specific literal expressions for common datatypes like Strings, Collections, which allows us to easily prohibit the object expression to create datatype values.
Trying to look for an alternative to the problem you pointed out for custom datatypes, I guess that what we could relax the instantiation expression to Classifier as you suggest, so that, at least we can do the following:
var var1 := new MyDatatype("abc");
Anyway, this looks like to be an obscure specification area (new and object constructs?, where is object exp body defined in the AS) ... which should probably require some more study to properly revise. I'll wait for your comments, and I'll do some more study on this the next week.
Cheers,\ Adolfo.
By Adolfo Sanchez-Barbudo Herrera on Oct 18, 2013 12:22
(In reply to comment #29)\
QVTo supports such cases by means of 'init{}' section. Since "8.2.1.15 MappingOperation" defines that implicit instantiation occurs right after 'init{}' section then one just have to assign 'result' in it.
For example, suppose you declare mapping which results in abstract classifier:
mapping String::mapToSequence() : EClassifier { }
Is it valid? Yes, but in order to invoke it one should assign 'result' in init section:
mapping String::mapToSequence() : EClassifier { init { result := object EClass{} } }
The same is applied to collection. You can find a number of junit test scripts that operate this way.
This is not helpful in my opinion, as it doesn't explain why we require to make object expression admit data types:
mapping String::mapToSequence() : Sequence(String) {\ init {\ result := Sequence(String) { "abc" };\ }\ }
By Ed Willink on Oct 18, 2013 12:37
(In reply to Sergey Boyko from comment #28)
(In reply to Ed Willink from comment #15)
(In reply to Christopher Gerking from comment #14) Because OCL specifies that distinct objects are not equal. (Remarkably the same as Java.)
In Java (new XXX() = new XXX()) is necessarily false. This is all that OCL is saying, and what QVTo is contradicting.
You're absolutely wrong. QVT (and OCL and MOF) is not Java.You specified a rule from another domain.
I'm not wrong. I am just specifying an analogy from Java that may help some peop[le realize that the eseentially identical ruule in OCL is not very surprising.
Distinct instances are compared according to their type. For instances of Class they are compared by references. For instances of DataType they are compared by value. That's it.
I couldn't agree more.
Neither OCL not QVT gives a way to compare instances of DataTypes by reference, they always compare by value.
In OCL 2.4. (In OCL <2.4 they actually compare by 'reference'/'identity'/... whatever you like really).
You seem to be ignoring that the problem is one of contradiction between equality on the left hand and difference on the right hand; not a problem with the meaning of equality.
By Adolfo Sanchez-Barbudo Herrera on Oct 18, 2013 12:43
(In reply to comment #30)\
Population section defines only the means to update existing object (that should be initialized in 'init{}' or otherwise will be created right before population section). See "8.2.1.24 ObjectExp" : If the variable is non-null, no instantiation occurs but the constructor body is used to update the existing code.
It uses "object x:X { … } // An explicit variable here" concrete syntax. Also there are number of 'population' usage in junit test scripts.
Your comment can be complemented with 8.2.1.15 MappingOperation (executing a mapping operation):
"2. At the end of the initialization section, an implicit “instantiation section” is entered, which provokes the instantiation\ of all the out parameters that are object instances and that still have a null value. Collection types are initialized\ with empty collections. By doing so, the corresponding relation trace tuple is populated. From that point the relation\ is considered to hold and the trace data becomes available."
mapping String::mapToList() : List(String) {\ population {\ // result has already been created and initialized to an empty list. Populate it\ result.add("abc");\ }\ }
Again, I don't see why we need to support collections instantiation via the object expression. The two following examples should be an error, IMO:
1) \ object myVar : Collection(String) { "abc" };
2) \ mapping C::mapping1() : Collection(String) {\ "abc" // The body of the implicit object expression\ }
They should be written as:
1')\ var myVar : Collection(String) { "abc" }
2')\ mapping C::mapping1() : Collection(String) {\ population {\ // Update the result including the "abc" String e.g:\ result := result->including("abc");\ } \ }
Cheers,\ Adolfo.
By Adolfo Sanchez-Barbudo Herrera on Oct 18, 2013 12:48
Again, I don't see why we need to support collections instantiation via the object expression. The two following examples should be an error, IMO:
Apologies. The examples were not the better one :P :
1) \ object myVar : Sequence(String) { "abc" };
2) \ mapping C::mapping1() : Sequence(String) {\ "abc" // The body of the implicit object expression\ }
They should be written as:
1')\ var myVar := Sequence(String) { "abc" }
2')\ mapping C::mapping1() : Sequence(String) {\ population {\ // Update the result including the "abc" String e.g:\ result := result->including("abc");\ } \ }
Cheers,\ Adolfo.
By Christopher Gerking on Oct 19, 2013 03:47
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #37)
mapping C::mapping1() : Sequence(String) { "abc" // The body of the implicit object expression }
...
mapping C::mapping1() : Sequence(String) { population { // Update the result including the "abc" String e.g: result := result->including("abc"); } }
This conversion is wrong. You can't reassign result from within the population section. See bug 388325 for details.
By Christopher Gerking on Oct 19, 2013 03:54
(In reply to Sergey Boyko from comment #30)
Christopher, could you please describe more in details under under what conditions collections are not persisted in trace.
Sorry, seems as if I expected a problem where there is none. Good to know that it works fine.
By Christopher Gerking on Oct 19, 2013 04:03
(In reply to Ed Willink from comment #35)
You seem to be ignoring that the problem is one of contradiction between equality on the left hand and difference on the right hand; not a problem with the meaning of equality.
Trying to bring light into the darkness, I understood Ed's chain of reasoning as follows:
By Christopher Gerking on Oct 19, 2013 04:14
(In reply to Christopher Gerking from comment #38)
(In reply to Adolfo Sanchez-Barbudo Herrera from comment #37)
mapping C::mapping1() : Sequence(String) { "abc" // The body of the implicit object expression }
...
mapping C::mapping1() : Sequence(String) { population { // Update the result including the "abc" String e.g: result := result->including("abc"); } }
This conversion is wrong. You can't reassign result from within the population section. See bug 388325 for details.
Bug 325192 is the better reference.
By Ed Willink on Oct 19, 2013 08:18
(In reply to Christopher Gerking from comment #40)
Trying to bring light into the darkness, I understood Ed's chain of reasoning as follows:
Yes.
By Adolfo Sanchez-Barbudo Herrera on Oct 20, 2013 12:56
(In reply to Christopher Gerking from comment #38)\
This conversion is wrong. You can't reassign result from within the population section. See bug 388325 for details.
Can´t remember reading about this in the spec during theses days. I´ll recheck tomorrow.
However, I have some comments which I´ll move to the issue Ed opened, since I consider they are more related to it.
Regards,\ Adolfo.
By Sergey Boyko on Oct 21, 2013 15:45
(In reply to Christopher Gerking from comment #40)
(In reply to Ed Willink from comment #35)
You seem to be ignoring that the problem is one of contradiction between equality on the left hand and difference on the right hand; not a problem with the meaning of equality.
Trying to bring light into the darkness, I understood Ed's chain of reasoning as follows:
- everything that results from an object expression is regarded as an object (makes sense)
- objects compare unequal
- if a collection results from an object expression, it is regarded as an object but compares equal (contradiction)
According to MOF everything is an Object whose Type (abstract classifier) can either be DataType (non-abstract) or Class (non-abstract).\ MOF defines equality relation separately for instances of DataType "compare by value" and for instances of Class "compare by reference".
OCL defines several properties that apply to all objects (respectively instances of DataType and Class):\ oclIsTypeOf (t : Classifier) : Boolean\ oclIsKindOf (t : Classifier) : Boolean\ oclAsType (t : Classifier) : instance of Classifier
To me the correct reasoning are the following:
Looking to the specific examples:
var t1 := Sequence{"1"};\ var t2 := Sequence{"1"};\ Object 't1' is equal to object 't2'. If we'll pass them to some blackbox method we will realize that 't1' and 't2' are the different objects.
var t1 := List{"1"};\ var t2 := List{"1"};\ Also object 't1' is equal to object 't2'. In this case it's obvious that in terms of references 't1' is not 't2'.
By Adolfo Sanchez-Barbudo Herrera on Oct 22, 2013 09:02
(In reply to Sergey Boyko from comment #44)\
According to MOF everything is an Object whose Type (abstract classifier) can either be DataType (non-abstract) or Class (non-abstract). MOF defines equality relation separately for instances of DataType "compare by value" and for instances of Class "compare by reference".
OCL defines several properties that apply to all objects (respectively instances of DataType and Class): oclIsTypeOf (t : Classifier) : Boolean oclIsKindOf (t : Classifier) : Boolean oclAsType (t : Classifier) : instance of Classifier \
To me the correct reasoning are the following:
- everything is an object with metaclass either DataType or Class
- objects are compared according to their types - instances of DataType by value, instances of Class by reference
- OCL has a notion of literal expression to define new object with metaclass=DataType
- QVT introduces two more constructions to define new objects - "object Classifier{}" and "new Classifier()".
Looking to the specific examples:
var t1 := Sequence{"1"}; var t2 := Sequence{"1"}; Object 't1' is equal to object 't2'. If we'll pass them to some blackbox method we will realize that 't1' and 't2' are the different objects.
var t1 := List{"1"}; var t2 := List{"1"}; Also object 't1' is equal to object 't2'. In this case it's obvious that in terms of references 't1' is not 't2'.
I completely agree with this. However, this doesn't allow us to give support to Collections within an ObjectExp
1) Current AS explicitly prohibit this(referredClass : Class), but even though we relaxed the AS to accept more general Type 2) You can't change the immutable OCL collection, which you are creating, with any statement inside the object expression body.
Note: There is a bug in the specifiction which failed to specified the "body : ConstructorBody" containment reference. QVTo implementation has fixed this and I'll send some QVTo issues shortly.
P.S: I've created some documentation, rationale, OMG issues to send, etc in the link bove. Feel free to add comments
https://docs.google.com/a/york.ac.uk/document/d/1KaqpzOrI67EZfevVqGo2x70pmWF2g9LNbAos0PLJ5u0
| --- | --- | | Bugzilla Link | 419339 | | Status | NEW | | Importance | P3 minor | | Reported | Oct 14, 2013 04:46 EDT | | Modified | Oct 22, 2013 09:02 EDT | | Version | 2.0 | | Reporter | Klaus Kleister |
Description
It is not possible to initialize an OrderedSet with object. For example:\ var temp := object OrderedSet(String){"abc"};\ assert fatal (temp->size() > 0);\ The assertion will fail because there are no elements in the set.