#Nested records and looping optional array.

1 messages · Page 1 of 1 (latest)

half lodge
#

I have records like this
type Allotment record {
string inventoryBlockName?;
AssociatedProfiles AssociatedProfiles?;
};

type AssociatedProfiles record {
Profile[] Profile?;
};

type Profile record {
string creatorCode?;
};

I want to loop the allotment.AssociatedProfiles.Profile

I tried foreach Profile profile in allotment.AssociatedProfiles?.Profile?, but compiler is not happy. How to do this?

#

is this how we need to do? it works, but looks verbose. Please let me know if there is another way to do this.

    if allotment.AssociatedProfiles !is () {
        AssociatedProfiles associatedProfiles = <AssociatedProfiles> allotment.AssociatedProfiles;
        if associatedProfiles.Profile !is () {
            Profile[] profiles = <Profile[]>associatedProfiles.Profile;
            foreach Profile profile in profiles {
                log:printInfo(profile.creatorCode);
            }
        }
    }
warm elk
#

Following code will simplify the logic

#

foreach Profile p in (allotment?.AssociatedProfiles?.Profile) ?: [] {
io:println(p.creatorCode);
}

half lodge
#

thanks, much better

clear trellis
#

Minor suggestion, if allotment?.AssociatedProfiles?.Profile is assigned to a variable and an is check is performed instead in an if-else statement, we can avoid creating an empty list if the field access results in ().

Profile[]? profiles = allotment?.AssociatedProfiles?.Profile;

if profiles is () {
    // Handle `profiles` being `()` due to `AssociatedProfiles` or `Profile` being `()`
} else {
    // `profiles` is `Profile[]`
    foreach Profile profile in profiles {
        io:println(profile.creatorCode);
    }
}

If we return, continue, etc. in the if block, the else block is not required, the type will be narrowed after the if block anyway.

Profile[]? profiles = allotment?.AssociatedProfiles?.Profile;

if profiles is () {
    // Handle `profiles` being `()` and return.
    return;
}

// `profiles` is `Profile[]` here.
foreach Profile profile in profiles {
    io:println(profile.creatorCode);
}
half lodge
#

got it, @clear trellis. Just wondering why the type is not narrowed if we do like this?

if allotment?.AssociatedProfiles?.Profile is () {
    // Handle `profiles` being `()` due to `AssociatedProfiles` or `Profile` being `()`
} else {
    // `profiles` is `Profile[]`
    foreach Profile profile in allotment?.AssociatedProfiles?.Profile {
        log:printInfo(profile.toString());
    }
}
clear trellis
#

Type is narrowed only when it is guaranteed to be safe to narrow, so narrowing happens only for local variables or parameters.

For example, with field access it could be possible that the value set to the field (and therefore the type) changes between when we do the is check and when we try to use it as if it were of the narrowed type. So type is not narrowed with field access. Here particularly, the fields could be removed since they are optional fields. Casting in such a scenario is also therefore not safe (the casts in the original approach will result in panics if the fields get removed after the is checks).

There is a proposal in the spec to make this work when the type of the value set to the field cannot change (i.e., final object fields and readonly fields in records) - https://github.com/ballerina-platform/ballerina-spec/issues/845 - but this isn't supported yet.

GitHub

Description: The type guard is not working for class variables of a union type, since we cannot guarantee the type remains the same due to possible concurrent modifications. Currently, the only way...