Hi
The original Bugzilla threads are a bit long and rambling. I hope my
draft proposed resolution below is consistent and contains no
surprises.
Regards
Ed Willink
"A compound assignment is equivalent to perform as much simple
assignments as there are expressions. Null values are skipped."
This is poor English and it conflicts with the OCL specification.
Pedantically it only skips "Null" values so "null values" are
allowed anyway.
"An assignment may receive a future variable produced by a
deferred resolve _expression_ (see ResolveExp). The effect is
equivalent to receive a null value except that a side-effect
action occurs in order to allow re-executing the assignment at the
end of the transformation.
An assignment _expression_ returns the assigned value unless it is a
future value, in which case null is returned."
This is barely intelligible and presumably related to the
SmartQVT implementation. It certainly has confusion about nulls.
I recommend following OCL and allowing null values.
->excluding(null) is easily added.
Issue 19238
AssignExp to an Collection should make clear that the results is
target->including(source)->excluding(null)
Issue 19687
The two examples
mymultivaluedproperty += object Node
{
}; // additive semantics
mymultivaluedproperty := object Node {
}
; // the list is reset and re-assigned
are simple assignments from single valued RHS. These are not
covered in the preceding description.
I consider both to be errors since the RHS is unsuitable for
assignment to the LHS. In both cases an asSet() or whatever should
be appended.
More generally, the library operation that exhibits the "additive
semantics" should be clearly identified.
Since addition to OCL collections is not permissible, it may be
necessary to define constructors, so that
var c : Set(String) := Set
{"a"}
;
invokes the Set(T)(Set(T)) constructor.
This would allow initialization for a Sequence from a List and
vice-versa that is otherwise magic.
which should exploit an OCL definition of OrderedSet::including
to be add at end if necessary.
Discussion - nulls
UML does not allow nulls in multi-values, therefore nulls must be
excluded during assignments to properties.
OCL does allow nulls, and even though this may appear to be a
mistake, QVTo must support nulls in OCL collections until OCL is
changed.
Discussion - multiple values
The current wording leaves open the design choice between:
a) perfect fidelity of nested types, i.e. addition of a
Collection of Collections adds the one Collection of Collection
element
b) perfect flattening, i.e. addition of a Collection of
Collections adds all the elements in the nested collections.
c) something in between, perhaps behaving differently depending
whether the LHS is a nested collection or not,
OCL unfortunately started without nested collections so that
imploicit-collect flattens requiring the explicit collectNested
for perfect Collection type fidelity. a) is not really an option.
c) risks a confusing anarchy. b) is what is often wanted. If users
want nested collections they can use more explicit operations.
Discussion - future values
Premature access to yet-to-be initialized values is obviously
bad. Making this an error is an option, but it would be a breaking
change; there may be users who poll the variable awaiting
initialization. Simplest to just clarify the words to indicate
that the value of any yet-to-be-initialized value is null, even
when null is not assignable to the value. It is not initialized to
null; it is null until initialized.
|
In 8.2.2.10 VariableInitExp add
Initialization of a multiple valued Variable uses the
value of the variable's initializer without conversion
or modification. This is unlike an AssignExp which
flattens multiple values and optionally replaces nulls.
The initializer must therefore conform to any explicit
Variable type.
In 8.2.2.11 AssignExp replace:
An assignment _expression_ represents the assignment of a
variable or the assignment of a Property. In this
description we
refer to “target field” the referred variable or
property. If the variable or the property is monovalued,
the effect is to reset
the target field with the new value. If it is
multivalued, the effect is to reset the field or to
append it depending on the
isReset property. If the provided value is made of more
than one _expression_, then the assignment is said to be a
compound assignment, otherwise it is a simple
assignment. An _expression_ in a compound assignment is
called a
contribution.
For a simple assignment, if the right-_expression_ is a
collection, assigning the variable or the property means
adding each
of the items of the collection (additive semantics).
Note that this is only valid for a multivalued target
field. Duplicate
elements are removed if the target field are Sets - this
is the case for property elements. In addition null
values are
automatically skipped.
A compound assignment is equivalent to perform as much
simple assignments as there are expressions. Null values
are
skipped.
An assignment may receive a future variable produced by
a deferred resolve _expression_ (see ResolveExp). The
effect is
equivalent to receive a null value except that a
side-effect action occurs in order to allow re-executing
the assignment at
the end of the transformation.
An assignment _expression_ returns the assigned value
unless it is a future value, in which case null is
returned.
by
An assignment _expression_ assigns or appends one or more
right hand side values to a left hand side Variable or
to a Property.
A simple single valued assignment assigns the single
value RHS to the LHS, optionally replacing a null value
by a defaultValue.
A simple multiple valued assignment assigns or appends
the multiple flattened RHS values to the LHS optionally
replacing null values by a defaultValue. In the case of
an assignment to a Property any residual null values are
omitted.
A complex assignment is equivalent to a
simple assignment comprising a Sequence of the compound
_expression_ values.
A deferred assignment is an assignment in
which one or more of the RHS expressions involves a
deferred resolve _expression_ (see ResolveExp). The entire
assignment is deferred until the late resolution has
been performed. Any premature access to the
yet-to-be-assigned Variable or Property yields a null
value.
For all assignments, the type of the LHS is unchanged
by the assignment.
The return value of an AssignExp is the value of the
RHS in the following equivalent assignments that clarify
the various possibilities.
append is one of the following operations: Bag::including,
List::append, OrderedSet::append, Sequence::append,
Set::including,
defaultValue is the AssignExp::defaultValue
late denotes the late keyword for a deferred
assignment or nothing otherwise.
left denotes the AssignExp::left property
identifying the LHS Variable or Property
LEFT denotes the type of left such as
Set(String)
value is the AssignExp::value property
identifying the RHS _expression_ value or values
Equivalent Single valued Property or Variable assign: left
:= value
left := late if value->at(1)
<> null
then value->at(1)
else defaultValue
endif
Equivalent Multiple valued Property assign: left
:= value
left := late value->flatten()->iterate(c;
acc :LEFT = LEFT{} |
let v = if c <> null then c else defaultValue
endif
in if v <> null then acc->append(v)
else acc endif)
Equivalent Multiple valued Variable assign: left
:= value
left := late value->flatten()->iterate(c;
acc LEFT = left |
let v = if c <> null then c else defaultValue
endif
in acc->append(v))
Equivalent Single valued Property or Variable append: left
+= value
invalid – append is not possible for single values.
Equivalent Multiple valued Property append: left
+= value
left := late value->flatten()->iterate(c;
acc : LEFT = left |
let v = if c <> null then c else defaultValue
endif
in if v <> null then acc->append(v)
else acc endif)
Equivalent Multiple valued Variable append: left
+= value
left := late value->flatten()->iterate(c;
acc : LEFT = left |
let v = if c <> null then c else defaultValue
endif
in acc->append(v))
|