Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[qvto-dev] QVT 1.3 AssignExp resolution

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


Description   

"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))






Back to the top