interface, nullable field, strict type check

typedef ISportProfile as interface {
    var sport as Activity.Sport?; // either nullable or not
    var subSport as Activity.SubSport?;
};

function useMeters(sportProfile as ISportProfile?) as Boolean {
    return sportProfile != null && /*sportProfile.sport == Activity.SPORT_RUNNING &&*/ sportProfile.subSport == Activity.SUB_SPORT_TRACK;
}

function foo() as Void {
    var currentWorkoutStepInfo = Activity.getCurrentWorkoutStep();
    var use1 = useMeters(currentWorkoutStepInfo /* as ISportProfile? */);
    var profileInfo = Activity.getProfileInfo();
    var use2 = useMeters(profileInfo);
}

depending whether I add or remove the "?" on the sport field in the interface I get a compilation error either because I pass WorkoutStepInfo that has sport as Sport or because I pass ProfileInfo that has sport as Sport or Null. Is there a way to make both work with strict type check?

ERROR: enduro3: Foo.mc:12,16: Passing 'PolyType<Null or $.Toybox.Activity.WorkoutStepInfo>' as parameter 1 of poly type 'PolyType<Null or interface { var subSport as Null or $.Toybox.Activity.SubSport; var sport as Null or $.Toybox.Activity.Sport; }>'.

or

ERROR: enduro3: Foo.mc:14,20: Invalid '$.Toybox.Activity.ProfileInfo' passed as parameter 1 of type 'PolyType<Null or interface { var subSport as Null or $.Toybox.Activity.SubSport; var sport as $.Toybox.Activity.Sport; }>'.

I feel like having a field's type "Foo?" and passing an object with that field as "Foo" should be allowed. Is this a compiler bug or is there some real reason that I am missing?

SDK 8.4.1

P.S: adding "(:typecheck(false))" annotation to useMeters doesn't help, but even if it would I wouldn't consider it a real solution

P.S2: casting WorkoutStepInfo to ISportProfile seems to work but still feels hacky

  • I feel like having a field's type "Foo?" and passing an object with that field as "Foo" should be allowed. Is this a compiler bug or is there some real reason that I am missing?

    The problem seems to be that you pass a Activity.ProfileInfo as argument where the argument is declared as 'PolyType<Null or interface { var subSport as Null or $.Toybox.Activity.SubSport; var sport as $.Toybox.Activity.Sport; }>'. In other words the compiler can not cast the ProfileInfo to your typedef type. Which is logical because they are totally different things. Even while Monkey C is loosely typed it cannot cast everything to anything. So you should cast as you suggest in PS2. But the best solution in my humble opinion is to do away with the typedef en declare the function as follows:

        
        function useMeters(sportProfile as Activity.ProfileInfo or Activity.WorkoutStepInfo or Null) as Boolean {
            return sportProfile != null && sportProfile.sport == Activity.SPORT_RUNNING && sportProfile.subSport == Activity.SUB_SPORT_TRACK;
        }
    

    This way you explicitly declare which Types your function accepts as an argument. I did not test this, it may be that in the function body you have to test for null on the sport and subSport level because in ProfileInfo both can be null.

  • LOL, this is funny! 1st of all, your solution works, I don't know how I didn't think about it!

    On the other hand you're also wrong: interface in Monkey C is a bit different than in Java. What I did above works, as I wrote, when the nullability of sport matches. So what you wrote is not the problem (that the "compiler tries to cast" ProfilerInfo to my typedef is factually wrong, it doesn't cast it. If the parameter passed have those fields, methods that is declared in the interface, and they "match", then it works).

    What's strange is that with:

    typedef NonNullType as interface { var b as Number;};

    typedef NullType as interface { var b as Number?;};

    class NonNullFoo { var b as Number;}

    class NullFoo { var b as Number?;}

    I can pass either Number or null to function a(Number?) {}

    I can pass NullFoo to function b(x as NullType) {}

    I can pass NonNullFoo to function c(x as NonNullType) {}

    but I can't pass NonNullFoo to function d(x as NullType) {}

      can you check if this is intentional or should I open a bug report about this?