- Collection Conversion
- Widening Cast at Your Own Risk
- When Is It Safe to Cast?
- Widening Conversion Copy
- Appending Collections
The collection type rules have changed from version 5.2 to version 6.0. They are now more strict. This affects sequences/sets/maps containing Objects. Collections containing values (like angle or point) or simple types (like int or double) are not affected.
Collection Conversion
As an example, this is no longer accepted:
Object[] seq = [Int: 1, 2, 3];
The reason for this is that it breaks the static type checking. Study this example:
Object[] seq = [Int: 1, 2, 3]; seq << Double(3.14);
This code compiles fine in 5.2 but the append of the Double causes a runtime exception when the code is executed. This is because the seq is not really an Object seq but rather an Int seq that has been cast.
Widening Cast at Your Own Risk
A consequence of the stricter conversion rules are that this kind of code pattern is no longer accepted:
Model3DSnapper{} mySet = getSomeSnappers(); snapAllAligned(mySet); // rejected because snapAllAligned is expecting Snapper{}
The most safe workaround for this is to copy the set to the correct element type, like this:
Model3DSnapper{} mySet = getSomeSnappers(); snapAllAligned(copySet(mySet, Snapper));
Or like this:
Model3DSnapper{} mySet = getSomeSnappers(); Snapper{} snappers(mySet.count); for (z in mySet) snappers << z; snapAllAligned(snappers);
However, the code will execute faster using a widening cast (avoiding the copy):
Model3DSnapper{} mySet = getSomeSnappers(); snapAllAligned(mySet.:Snapper{});
But it's very important that you understand that when you write this conversion, you bypass the static type checking and thus risk encountering a runtime exception. This will occur if the called function (
snapAllAligned() in this instance) tries to append a Snapper to the given set (because the set only accepts Model3DSnapper).
When Is It Safe to Cast?
It's always safe to cast at append. Example:
Model3DSnapper{} mySet = getSomeSnappers(); snappers += mySet.:Snapper{};
It's also safe to cast when you are certain that the function you are calling (or any function that it can reach) will not append any elements to the passed collection. One good example is the primary constructor in ObjectSubSet:
public constructor(Object[] seq) { if (seq) { Object[] clone(seq.count); clone += seq; this._members = clone; } else { init _members(); } }
As you can see, this constructor itself performs a shallow copy of the seq, and it does not keep any reference to the seq either, so we can be certain that the seq cannot in any way be modified as an Object[] here.
Another example is when you are calling a sort function (this will not append any new elements to the collection, just change the order). If we have these definitions:
public class BiFaciasBase { ... } public class BiFacias extends BiFaciasBase { ... } public int faciasCompare(BiFaciasBase r0, BiFaciasBase r1, Object this) { ... }
We could previously call sort like this:
BiFacias[] fs = getFacias(...); fs.sort(function faciasCompare, this); // will not compile in 6.0 return fs;
But now we will have to do a widening cast, like this:
BiFacias[] fs = getFacias(...); (fs.:BiFaciasBase[]).sort(function faciasCompare, this); return fs;
Widening Conversion Copy
In order to actually convert your Model3DSnapper[] to a Snapper[] for instance, there are two new macros available:
- copySeq(seq, type)
- copySet(set, type)
Examples:
Int[] intSeq = [Int: 11, 22]; Object[] objSeq = copySeq(intSeq, Object); Int{} intSet = {Int: 11, 22}; Object{} objSet = copySet(intSet, Object);
Note that this is not the same as just casting with "intSeq.:Object[]".
Appending Collections
Appending an Object seq/set to another Object seq/set using the << operator is no longer valid.
Object[] seqA(); Object[] seqB = [Object: Int(111), Double(3.14)]; //seqA << seqB; // no longer accepted seqA += seqB; pln(seqA);
This is because the operation is ambiguous. What we want to do is (usually) to append the elements of seqB to seqA, but since seqB actually is an Object in itself, we could also mean to append the sequence itself, rather than the elements. To get around the problem, please use the += operator instead (this will always append the elements). Also note that += is null safe (in the example above seqB may be null, but seqA must exist).
Skipping Null Elements
In 5.2 you could skip null elements byt using the <<? operator. In 6.0 use the +=? operator instead.
// Include all elements //seqA << seqB; // old 5.2 code seqA += seqB; // new 6.0 code // Exclude null elements //seqA <<? seqB; // old 5.2 code seqA +=? seqB; // new 6.0 code
Appending Collection by Reference
In the unlikely event that you actually want to append the collection itself, it can be done like this:
Object[] seqOfSeqs(); Object[] seqA = [Object: Int(11)]; Object[] seqB = [Object: Int(22)]; seqOfSeqs << seqA.:Object; seqOfSeqs << seqB.:Object;
Membership Test
You can no longer use incorrect types for contains and in. This is rejected in 6.0:
Int[] ints = [Int: 1, 2, 3]; Object v = ints[1]; pln(v in ints);
You must instead write:
Int[] ints = [Int: 1, 2, 3]; Object v = ints[1]; pln(v.Int in ints);
This is a side effect of the stricted type rules (the former code was working fine in 5.2). but below is an example of what you also could do in 5.2, an operation that doesn't make any sense at all:
public bool foo(Snapper[] snappers) { return 3.14 in snappers; }
// results in a compile error in 6.0
Please sign in to leave a comment.