Hi Christopher,
I think you are not looking close enough to the disjunct
relationship direction. I said "100 mappings disjuncting B::doIt()",
so B::doIt doesn't "act as dynamic dispatch" in any case. I can
still be clearer/more specific/precise with the example, though.
Please have a look to the following:
mapping A1::doIt() : AA disjuncts B::doIt, C::doIt { ... }
mapping A2::doIt() : AA disjuncts B::doIt, C::doIt { ... }
...
mapping A100::doIt() : AA disjuncts B::doIt, C::doIt { ... }
mapping B::doIt() : BB {...}
mapping C::doIt() : CC {...}
Having B and C (no inheritance relationship between B and C)
inheriting A1, A2, ... A100 (no inheritance relationship between the
different A1, A2... A100 )
If you have :
var a1 : A1 := Object B {... }
a1.map doIt() -> Stactic call on A1::doIt() (-> create trace
record)
Dynamic call on B::doIt() (-> create
another trace record)
the object hold by var a1 should be traced on an BB object, through
the mappings A1::doIt() and B::doIt(), therefore the following
boolean expressions must hold:
a1.resolveIn(A1::doIt) <> null
a1.resolveIn(A2::doIt) = null
...
a1.resolveIn(A100::doIt) = null
a1.resolveIn(B::doIt) <> null
a1.resolveIn(C::doIt) = null
If you have :
var b : B := Object B {... }
b.map doIt() -> Static call on B:::doIt() (-> create trace
record)
Dynamic call on B::doIt() (-> trace
record already created)
the object hold by var b should be traced on an BB object, through
JUST the mapping B::doIt(), therefore the following boolean
expressions must hold:
b.resolveIn(A1::doIt) = null
b.resolveIn(A2::doIt) = null
...
b.resolveIn(A100::doIt) = null
b.resolveIn(B::doIt) <> null
b.resolveIn(C::doIt) = null
Sorry, but I'd need a concrete and strong real world example to
convince me that what I've exposed above is not right.
Have a nice weekend !!
Cheers,
Adolfo.
On 09/10/2015 15:35, Christopher
Gerking wrote:
No.
If there are 100 mappings and B::doIt acts as dynamic
dispatch candidate for all of them, I expect all possible
resolveIn(… ::doIt) calls to consider B::doIt as well. No
need for 100 trace records, but the one existing record
should be taken into account in all 100 cases.
IMHO
the statically known type of an object shouldn’t affect the
subsequent resolveIn behavior. If there is a dynamic
dispatch on invocation, there should be an inverse dynamic
“recipience” on resolve.
Hi
Christopher,
Well, since statically is known, a text-hover can highlight
which mapping you are (statically) calling.
The problem is the direction of the relationship. Let me
clearly expose the point with another example:
If there are 100 mappings disjuncting B::doIt(), are you going
to create a 101 trace records, when just one B::doIt() mapping
call took place ?... Surely not
Regards,
Adolfo.
On 09/10/2015 12:49, Christopher Gerking
wrote:
Hi
Adolfo
Yes,
I recognized your point only after sending my last
message.
Again
I can’t come up with a real example. But I could imagine
that there is a number of :A instances and doIt() is
called on all of these. If (for whatever reason) some of
the :A is known as :B on invocation, a subsequent
resolveIn(A::doIt) will return null in these cases.
To
circumvent this behavior we would need b.oclAsType(A).map
doIt() in order to ensure that A::doIt is consulted first.
That’s quite a burden for users, because the difference
between a call to A::doIt and a call to B::doIt is totally
invisible in the CS.
I
propose to define the resolveIn behavior in such a way
that candidate mappings for a dynamic dispatch are
considered as well.
Regards
Christopher
Hi All,
Ed: After doing a quick excercise in Eclipse QVTo, The only
"shortcoming" I can oversee to the new addition (the
disjuncting mapping body can be executed), is the following:
mapping EClass::m1() : EClass
disjuncts EClass::m2, EClass::m3{}
There is a backwards compatibility issue, because now m1
mapping, would return an EClass rather than null. Note that,
even though you don't define init/population section, the
object would be created.
Providing that "default" disjuncts mapping could still be
created by the transformation designer, I'd not push forward
that semantic modification for a mapping disjunct.
Christopher: I mentioned my point of view for your example,
perhaps you missed it/it was not clear. Summarizing:
b : B;
b.doIt();
Should statically invoke B::doIt() (the disjuncted mapping)
Therefore, A::doIt() is not invoked
Therefore, IMO opinion, b.resolveIn(A::doIt) should return
null rather than the object created by B::doIt().
Another comment, traces are needed at the moment in which
the mappings are executed for both, disjuncting and
disjuncted mapping, because if you only rely on tracing the
disjuncting mapping and the disjuncted mappings have guards,
and the original object is modified (inout model), you won't
be able to accurate know which disjuncted mapping was
actually been executed (if you try to discover that when
evaluating the resolveIn _expression_).
Regards,
Adolfo.
On 09/10/2015 10:20, Ed Willink wrote:
Hi
Christopher
My suggestion that the disjuncting body replaces the
currently null no-match default seems more elegant; no
built in null, an opportunity to define a useful 'null'
behavior. But if anyone dislikes it, the status quo is
adequate.
QVT13-23 is one that I want to resolve this weekend. See
List and Dict thread for requested table of current
practical usage.
Regards
Ed Willink
On 09/10/2015 09:09, Christopher
Gerking wrote:
Hi
Should
there be a “disjuncting body” or not? Eclipse QVTo
enforces statically that there is none. Where are the
advantages/disadvantages?
With
respect to the tracing, I prefer trace records for
both disjuncting and candidate mappings. See
http://solitaire.omg.org/issues/task-force/QVT13-23.
Eclipse
QVTo does that already.
I
would like to understand the tracing behavior for
dynamic dispatch. If there is some ‘a’ known as :A,
and a.doIt() is called, a trace record for A::doIt
should be obviously created. If ‘a’ is actually :B and
there is a dynamic dispatch, then another trace record
for B::doIt should be created as well.
But
what happens if there is some ‘b’ known as :B and we
call b.doIt(). Clearly there is a trace record for
B::doIt, but what about A::doIt?
No
matter how many trace records are created, I think
that b.resolveIn(A::doIt) should work. Instead of
creating multiple trace records, another option would
be to make the resolveIn more sensitive: when calling
b.resolveIn(A::doIt), trace records for the candidate
B::doIt could be considered as well.
Regards
Christopher
Hi
Thanks. Three interesting points. We have 6 days before
this gets semi-frozen in a Preview.
It is not my intention to require any run-time
functionality to move to comple-time. I think I just
highlight that unspecified but necessary functionality
is at run-time.
The "usual object-oriented virtual call semantics" is:
execute the function corresponding to the derived type.
My suggested MappingCallExp semantics is a prioritized
(run-time) disjunct search:
if A's predicates are satisfied doA
else if B's predicates are satisfied doB
...
else null
I observe that some static analysis can eliminate some
of the cases. (In the case of unknown transformation
extension, the static analysis could be at load-time, or
JIT for each new invocation.)
In your example perhaps your internal doIt' is my
internal list of candidates. Reifying the internal list
may make the exposition clearer.
---
Given a body for both candidate and disjuncting mapping,
when is each executed? Perhaps the disjuncting body
(default null) is executed only when no candidate is
found.
---
If a MappingCallExp.referredOperation references X::doA,
it is currently unspecified in OCL and so in QVTo
whether this invokes precisely X::A or the overload
applicable to the actual source type. In OCL it should
nearly always be the overload, though just occasionally
as in super-calls it would be nice to suppress dynamic
dispatch. Perhaps a Boolean isStaticDispatch flag is
needed and set by the presence of a fully qualified name
in the source code.
If a MappingCallExp used the "usual object-oriented
virtual call semantics" then it is precisely the same
problem. But it's different although with the same
ambiguity. Does the the AS reference to X::A trigger the
disjunct search for X::A or just call X::A regardless?
Mostly it should do the disjunct search, but just X::A
if we want a super-call. (A more optimized
implementation looks up the precomp[uted source type
specific list of candidate mappings.)
---
Tracing has a similar problem.
Is the disjuncting mapping traced or is the actual
selected candidate mapping traced? Both are of some use.
See my AMT 2015 talk. The most important usage is in
resolveIn where the user cannot sensibly reference the
candidate mapping, therefore resolveIn must at least
work for a reference to the disjuncting mapping.
However, particularly given that a candidate mapping may
actually be candidate for multiple disjuncting mappings,
it should also be possible to invoke resolveIn with
respect to the actual candidate mapping. This is
probably difficult and hard to support unless the trace
record contains both disjuncting and candidate mapping
identities. Both are needed internally, an arbitrary
choice could be made for the qvttrace file for which the
QVTo specification might evolve to dismiss as a
proprietary convenience. Certainly nothing that is QVTr
compatible and fully supporting incremental
re-execution.
Regards
Ed
On 07/10/2015 11:31, Adolfo
Sanchez-Barbudo Herrera wrote:
Hi Ed,
In principle, the implicit disjuncts seems a good
idea, an there are some interesting clarifications in
your proposal, but I need to point out a couple of
concerns.
1. The disjuncting mapping itself is a kind of
"intermediate" mapping (no logic goes there). So in
your example:
mapping A::doIt() : AA {...}
mapping B::doIt() : BB {...}
mapping C::doIt() : CC {...}
The equivalent should be something like the following:
mapping A::doIt'() : AA
disjuncts B::doIt, C::doIt, A::doIt {...}
mapping A::doIt() : AA {...}
mapping B::doIt() : BB {...}
mapping C::doIt() : CC {...}
At
compilation time any static call to A::doIt(), should
be changed to the A::doIt'()
2. More seriously, you are moving a run-time dispatch
mechanism, to a compile-time, which it should turn to
be reflected in the AS. That in principle sounds OK
for single transformations, i.e. for
inter-transformation mappings extensions, but it
doesn't seem so good in the inter-transformation reuse
scenario, because we need cross-references from the
extended transformation to the different extending
ones. Compiled transformations wouldn't make less
sense, or we would require many AS compiled
copies/variants of a extended transformation,
depending on how these implicit disjuncts come up from
the extending ones.
I reluctant to replace the traditional virtual mapping
call mechanism (performed at runtime) by this new
implicit disjuncts mechanism (performed at compile
time). That said, I think that much of what you have
tried clarify, can be useful explain for instance, how
signature of extending mappings should be, or how the
virtual mapping call mechanism should be performed in
QVT rather than "This follows usual object-oriented
virtual call semantics" (page 93) .
I hope you find my comments useful.
Regards,
Adolfo.
On 07/10/2015 10:41, Ed Willink
wrote:
Hi
A first attempt at a proposal. I think it says what
is needed, but perhaps some of it should be
moved/duplicated in 8.2.1.21 MappingCallExp.
I considered "abstract" since it is in the Concrete
Syntax and another issue requests it. I'm not sure
whether it is really that useful.
Regards
Ed Willink
Replace Section 8.1.13 with the following text to
be placed before the current 8.1.12.
8.1.12 Mapping Overloading
Invocation of a mapping selects a disjunction
of one or more candidate mappings at
compile time. At run-time, the first matching
candidate mapping is selected and
invoked. The disjunction may be specified
explicitly using the
disjuncts keyword or implicitly by an
overloaded mapping.
8.1.12.1 Explicit Disjuncts
In the following example, the explicit
disjunction defines convertFeature as
a
disjuncting mapping name that may be
invoked on a UML::Feature with a
Boolean argument. convertAttribute,
convertConstructor and
convertOperation are candidate
mapping names.
mapping UML::Feature::convertFeature(asUpper: Boolean) : JAVA::Element
disjuncts convertAttribute, convertOperation, convertConstructor {}
mapping UML::Attribute::convertAttribute(asUpper: Boolean) : JAVA::Field {
name := if asUpper then name.toUpper() else name endif;
}
mapping UML::Operation::convertConstructor(asUpper: Boolean) : JAVA::Constructor
when {self.name = self.namespace.name;} {
name := if asUpper then name.toUpper() else name endif;
}
mapping UML::Operation::convertOperation(asUpper: Boolean) : JAVA::Constructor
when {self.name <> self.namespace.name;} {
name := if asUpper then name.toUpper() else name endif;
}
The explicit disjuncts causes the
mapping invocation to successively assess the
implicit and explicit predicates of
convertAttribute, convertConstructor
and convertOperation to identify the
first match. If no match is found the mapping
invocation returns
null.
The explicit predicates are provided by
arbitrary constraints specified in
when clauses. Implicit predicates are
provided by the type signatures; each source and
argument must conform to the type of the
disjuncting mapping. An abstract
mapping has a false implicit
predicate; an abstract mapping cannot be
executed.
The candidate return type must be
covariant, that is the same as, or derived from
that of, the
disjuncting return type to ensure that
no result incompatibility arises.
Since the argument types contribute to implicit
predicates, the candidate argument
types may be supertypes or subtypes of the
disjuncting mapping. The number of candidate
and disjuncting argument types must be
the same.
An explicit candidate mapping is
identified by its mapping identifier which
identifier may contribute to more than one
disjunction.
8.1.12.2 Implicit Disjuncts
An implicit disjunction groups overloaded
mappings. One mapping overloads another when the
overloading source type extends the overloaded
source type and when the overloading and
overloaded mappings have same name and argument
count.
When UML::Attrbute and UML::Operation
extend UML::Feature, the previous
example may be simplified to use an implicit
disjunction.
mapping UML::Feature::convertFeature(asUpper: Boolean) : JAVA::Element {}
mapping UML::Attribute::convertFeature(asUpper: Boolean) : JAVA::Field {
name := if asUpper then name.toUpper() else name endif;
}
mapping UML::Operation::convertFeature(asUpper: Boolean) : JAVA::Constructor
when {self.name = self.namespace.name;} {
name := if asUpper then name.toUpper() else name endif;
}
mapping UML::Operation::convertFeature(asUpper: Boolean) : JAVA::Constructor
when {self.name <> self.namespace.name;} {
name := if asUpper then name.toUpper() else name endif;
}
he explicit disjuncts provides distinct names
and so facilitates explicit calls direct to the
candidate mappings. The implicit
disjuncts requires no disjuncting
declaration and so faciltates extension by
addition of further contributions.
8.1.12.3 Disjunct candidates
All mappings with the required name, argument
and matching or derived source type are
candidate mappings for the invocation
of a disjuncting mapping. This
includes mappings inherited from extended
transformations. The
candidate mappings referenced in a disjuncting
mapping may introduce new names and consequently
a further disjunction of
candidate mappings; the explicit
disjunct is transitive.
For instance invocation of convertFeature
for a Property in the explicit
disjuncts example should consider a
Property::convertOperation(Boolean)
inherited from an extended transformation since
the explicit disjunct adds
convertOperation to the transitive
candidates. Conversely, the implicit disjunct
example considers only candidates whose
signature is
convertFeature(Boolean).
For non-strict evaluation, a deterministic
evaluation order for evaluation of the
predicates of the candidates as guards is
established by sorting using the following
proritized criteria. A distinction by an earlier
criteria overrules all later criteria.
-
directly invoked explicitly disjuncted
candidate mappings are evaluated in
declaration order
-
mappings in the current transformation are
evaluated before those in an extended
transformation, then mappings in an extended
transformation before those in an extended
extended transformation, and so forth
-
mappings for a more derived type are executed
before those for a less derived type
-
mappings are prioritized by alphabetical
mapping name order
-
mappings are prioritized by alphabetical
context type name order
-
mappings are prioritized by alphabetical
context type containing package name order,
then by containing package containing package
name order, and so forth
The ordering above ensures that an extending
transformation can occlude a mapping in an
extended transformation and that a mapping for a
derived type occludes that for a base type. An
implementation may use static analysis of the
predicates to eliminate occluded candidates
completely and to provide reduced candidate
lists according to the source type of the
mapping invocation.
For strict evaluation, the same ordering
applies but the first candidate for which the
source type conforms is selected without
evaluating the predicate as a guard. The
predicate is instead evaluated as a
pre-condition giving a
null return when not satisfied.
In the QVTo model and Fig 8.3. Add
MappingOperation ::isAbstract : Boolean[1]
default false.
In 8.2.15 MappingOperation Attributes Add
isAbstract : Boolean[1]
Indicates whether the mapping is abstract,
requiring an overload for all derived context
types. Default is false.
On 06/10/2015 13:26,
Christopher Gerking wrote:
Hi
+1 for choosing the most derived in case of a family of overloaded mappings.
I also like the approach of defining an overload as an implicit disjuncts. In particular, this could simplify the traceability recording rules.
When calling a.doIt() dispatches to B::doIt(), I expect a traceability record for a.resolveIn(A::doIt), even if A::doIt didn't really execute.
For disjuncts, Eclipse QVTo already records traceability links also for the disjuncting mapping. Therefore the above requirement would be fulfilled.
It is more interesting when we have b:B and call b.doIt(). Does it still consult A::doIt() and produce the traceability link for b.resolveIn(A::doIt) ?
Regards
Christopher
-----Ursprüngliche Nachricht-----
Von: qvto-dev-bounces@xxxxxxxxxxx [mailto:qvto-dev-bounces@xxxxxxxxxxx] Im Auftrag von Ed Willink
Gesendet: Sonntag, 4. Oktober 2015 13:30
An: QVTOML developer mailing list <qvto-dev@xxxxxxxxxxx>
Betreff: [qvto-dev] What are the mapping refinement rules
Hi
When preparing my "QVT Traceability : What does it really mean?"
presentation to AMT 2015, I was forced to think hard about mapping signatures and their relationships.
https://www.eclipse.org/mmt/qvt/docs/ICMT2014/QVTtraceability.pdf
http://www.slideshare.net/EdWillink/qvt-traceability-what-does-it-really-mean
Declaratively, independent mappings are all invoked independently, dependent mappings such as refinements are arbitrated by their predicates to select the best of a group of related mappings.
Imperatively, it is much simpler, mappings are explicitly invoked, so exactly one compatible mapping is invoked per source object.
The simplest case is the invoked name corresponds to a declared mapping.
More interesting, the invoked name corresponds to a disjuncted mapping allowing the best match of the explicit disjuncts to be invoked, else null.
I am unclear about what happens when the invoked name corresponds to a family of 'overloaded' mappings analoguous to Java operation overloads.
e.g.
mapping A::doIt() : AA {...}
mapping B::doIt() : BB {...}
mapping C::doIt() : CC {...}
where C and B extend A
I presume that in QVTo we choose the most derived analoguously to Java.
I'm inclined to clarify this situation by defining such overloads as implicit disjuncts, so that all colliding names contribute to a disjunct
The example is then equivalent to
mapping A::doIt() : AA
disjuncts B::doIt, C::doIt {...}
mapping B::doIt() : BB {...}
mapping C::doIt() : CC {...}
This could make disjuncts extensible since an extending transformation could supply additional name collisions for the explicitly/implicitly disjuncted mapping.
In order to give deterministic dispatch, the order of disjuncts is:
explicit disjuncts first, then implicit disjuncts successively ordered by the following criteria
- most derived source type,
- most derived first argument type,
- most derived second argument type,
- etc
- alphabetically by containing class name,
- alphabetically by containing class' containing package name,
- etc
The signatures of refinements are unclear. Since a mapping is explicitly invoked, we clearly require that disjuncting mappings have the same number, position and direction of arguments and a covariant
(same/derived) return type. (An explicit disjunct may omit trailing in
arguments.)
Since we are invoking mappings with predicates rather than invoking operations, we may allow disjuncting mappings to also have covariant or even contravariant arguments. When a disjuncting mapping has a distinct in/inout argument type, it is equivalent to an oclIsKindOf() when predicate for that argument type with a subsequent oclAsType() in the body.
Regards
Ed Willink
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit https://dev.eclipse.org/mailman/listinfo/qvto-dev
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
-----
No virus found in this message.
Checked by AVG - www.avg.com
Version: 2015.0.6140 / Virus Database: 4435/10768 - Release Date: 10/06/15
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
No
virus found in this message.
Checked by AVG - www.avg.com
Version: 2015.0.6140 / Virus Database: 4435/10773 -
Release Date: 10/07/15
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
No
virus found in this message.
Checked by AVG - www.avg.com
Version: 2015.0.6140 / Virus Database: 4435/10783 -
Release Date: 10/08/15
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
_______________________________________________
qvto-dev mailing list
qvto-dev@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/qvto-dev
|