Impossible to use Attention.playTone/vibrate with local vars and strict type checking?

With type checking set to strict, code such as:

var profile = new Attention.ToneProfile(200, duration * 1000);
Attention.playTone({:toneProfile => [profile]});

results in:

Invalid '$.Toybox.Lang.Dictionary{:toneProfile as $.Toybox.Lang.Array<Any>}' passed as parameter 1 of type 'PolyType<$.Toybox.Attention.Tone or {:repeatCount as $.Toybox.Lang.Number, :toneProfile as $.Toybox.Lang.Array<$.Toybox.Attention.ToneProfile>}>'.

because apparently, contents of arrays are always inferred as Any (?), and it looks like that's not compatible with the signature in strict mode (specifying a :repeatCount doesn't help).

However, it does not seem possible to be more explicit either:

var profiles as Array<ToneProfile> = new Attention.ToneProfile(200, duration * 1000);
Attention.playTone({:toneProfile => profiles});

results in:

Invalid explicit typing of a local variable. Local variable types are inferred.

So there seems to be no way to make this work. Am I missing something, or is that just not possible?

  • Two new observations after looking at this some more:

    • This happens even when setting the type checking level to "Gradual"
    • And it happens with VibeProfile too, which makes for a bit simpler example code:

    var profile = new Attention.VibeProfile(intensity, duration * 1000);
    Attention.vibrate([profile]);

    results in:

    Invalid '$.Toybox.Lang.Array<Any>' passed as parameter 1 of type '$.Toybox.Lang.Array<$.Toybox.Attention.VibeProfile>'.",

    This is with SDK 4.1.5 in VS Code on Linux.

  • What happens when you do `profile as VibeProfile` in the code? Either in the array contstruction or when assiging the value to profile? 

  • Here's how you can make this work using the example you've provided:

    var profile = new Attention.VibeProfile(intensity, duration * 1000);
    Attention.vibrate([profile] as Array<VibeProfile>);
    The key here is that profile is a VibeProfile object, but Attention.vibrate() is expecting an array of VibeProfiles. You recognize this, of course, because you're giving it [profile] as an argument, but unless you explicitly cast it as an array that contains VibeProfiles, it will just be an array that contains Any type, which causes the type mismatch. As an aside, if you were attempting to create the profile array at the class level (so not at the local scope), you would need to do this:
    var profile as Array<VibeProfile> = [new Attention.VibeProfile(intensity, duration * 1000)] as Array<VibeProfile>;
  • Sadly, that does not work - both still cause the array to be inferred as Array<Any>.

  • That does indeed work - thanks!

    However, it seems rather cumbersome and error-prone: In situations where I'd need [profile] (or an equivalent Array argument somewhere) in multiple places in a function, I'd need to specify the type everywhere I'm using it.

    In my opinion, typing of arrays would work like this in an ideal world:

    • An array literal with no elements ([]) gets inferred as Array<Any>
    • An array literal with elements in it gets inferred as Array<InnerType>, where InnerType is a Poly type of all inferred element types - thus, [profile] would have been inferred as Array<VibeProfile>.
    • In situations where an array gets initialized empty (or with InnerType1), and later filled with InnerType2 (e.g. var array = [1]; array.add("x");), there is an easy way to specify the desired type when declaring it: var array as Array<Number> = [];

    This would both make the inference more powerful, as well as provide an easy way to override it at the declaring site - clearly, the inference can't always know what's going to be inside an array.

  • Glad that helped. :) I'll pass along your feedback for consideration.

  • Personally, if you have var array as Array<Numeric> and you than later on do array = [] I would want to see it inferred to as Array<Numeric>. If you enter array = [ "Some string" ] it should warn under informational and error under the higher bits and pieces.You could even argue that it needs to error in informational.

  • I'm going to start with an agreement that typed containers (specifically arrays) need improvement. The faux-generic approach we took allows for them to be typed, but neither lends itself to type-inferencing or common Monkey C patterns. It's a little worse because other parts of type-inferencing work pretty well, so containers stand out because they require explicitness. 

    Right now our focus is finalizing the compiler so that you all can take advantage of the optimizer in production apps. In the meantime we're taking feedback to prioritize what improvements to do first. Improving container typing is high on that list.