Monkey C: 0.0 treated as true value

Environment: CIQ 3.0.5
(Tested in both CIQ1 and CIQ2+ watches in simulator. Known to happen on real CIQ2+ watch)

Unlike C, Javascript and probably every other programming language in the world, 0.0 is treated as a true value.

Also, 0.0 is treated as true, but "0 == 0.0" is true (as it should be), which is logically incoherent. If x == y, one can't be true while the other is false.

I can't even post the code to the forums (as per usual), so it's all on pastebin:
https://pastebin.com/KKZGUu3m

Output:
0 == 0.0: PASSED....
0.0 is truthy: FAILED....
0 is falsey: PASSED....



All I can say is that Monkey C and Connect IQ have really laudable design goals and great potential, but I think stuff like this is a huge disincentive to devs. There's no substitute for testing, but we shouldn't have to test obvious language constructs to see if they work the same way as expected.

Furthermore, we still can't post code in the forums.... A huge disincentive to participate in the community. The amount of effort I had to expend just to post this bug report is ridiculous.

I get that Garmin has higher priorities, like promoting the Spider-Man watchface (hey, I liked Spider-Man: Homecoming) and not updating the Uber/Lyft apps.
  • This means to implement a simple division function "x / y" it's not enough to test "if [y]", you have to test that y is not null and not zero.

    [Where y could be an integer, float or null, which is plausible if you are doing math on ActivityInfo.]

    On a side note, it looks the language design bug I posted last year where logical OR is treated like bitwise OR is still not fixed. Guessing it never will. (I understand that you don't want to break existing code in the field.)
    https://pastebin.com/M9q9Vcda

    Output:
    x = null
    y = 0

    Trying if [x]...
    if [x] works
    Trying if [y]...
    if [y] works
    Trying if [x && y]...
    if [x && y] works
    Trying if [x || y]...
    Unhandled Exception
  • Former Member
    Former Member over 6 years ago
    I can provide some information here, but I can only comment on the way things are, and not why they are that way.

    Regarding your new issue. As best I can tell, floats just simply are not allowed to be interpreted as true/false values. You should definitely avoid attempting to do so. Based on what I can see, I would really expect some sort of exception or failure in the case where you attempt to evaluate if( 0.0 ). As far as I can tell, it is not considered to be true or false, so behavior here is probably a bit undefined. Some sort of ticket is probably in order since the system appears to just be ignoring this type of evaluation instead of producing an error.

    Your prior issue is similar, but I think there is more going on than you tested for. The handling of AND and OR is a bit unique in Monkey C. Logical OR/AND will be applied in any case where a boolean value is involved, and bitwise or will be used in cases involving only Number and/or Long. Again any other types are not valid for this operator. this includes null. It actually isn't allowed in the && case either, and I suspect your test is only passing due to short circuit evaluation. If you tried "var foo = x && y; if(foo);", I think you would see the same exception.


    The key to avoiding these types of issues appears to be keeping all your null checking separate from other logic.
  • Brian.ConnectIQ Thanks for the quick and patient response. Sorry for the tone of my original post.

    I guess my problem is that in the limited memory space of CIQ1 and even CIQ2 datafields, sometimes a handful of null checks will make the difference between a datafield that runs and a datafield that crashes. I've actually rewritten some logic to initialize with negative numbers instead of nulls to reduce the number of null checks and save memory. (To be fair, in that case it wouldn't have mattered if null could be used with logical AND/OR).

    I think it's possible that the thing with logical/bitwise AND and OR may have been explained last year (when I probably posted a much more extensive test case), but I think at the time it was a surprise to even some regulars (e.g. Travis). But it is the way it is. I guess it probably has something to do with efficiency (size of VM), or perhaps it's a safety net for new coders. I guess I'll never know and I guess it doesn't matter.

    I guess I bring these issues on to myself by:
    - Not doing adequate unit testing
    - Trying to pack too many features into tiny data fields (less is more)

    The key to avoiding these types of issues appears to be keeping all your null checking separate from other logic.


    Yeah, I guess I sort of learned that the hard way, but the thing with 0.0 was sort of a new wrinkle.

    As I probably commented before, I guess I just sort of wish this kind of thing was documented somewhere. Maybe in an appendix, so it doesn't scare off noobs, but at least it would be there for people who could appreciate that kind of information.

    Guess I will have to stop assuming that Monkey C is like C or Javascript, when there is no reason to do so.

    Thanks again!
  • Former Member
    Former Member over 6 years ago
    You definitely found a bug worth reporting here, so it is a good find. For your own amusement, try this out:
    var x = 0.0f;
    do {
    Sys.println( "is 0.0f truthy?" );
    } while( x );

    Sys.println( "nope, I guess 0.0f is false" );


    In some cases Monkey C is trying to be everything. In some cases it makes things easy because it just works the way you expected it to based on that other thing you know, but in other cases, it works like that other thing you don't know.

    This case can be compared to Java, which does not allow you to test things that are not boolean. However, Monkey C allows integers, which isn't fully consistent with Java... and it is not producing an error for floats, and a number of other things that it should be producing an error for.



  • Brian.ConnectIQ thanks for the explanation. I've coded in a lot of languages, but I have to admit I probably think in terms of C/C++/C# and Javascript most often, although I like a lot of things about other languages.

    Ironically, I can (almost) relate to the difficulty of designing a language, since I wrote a data field that implements a simplified programming language on top of Monkey C >_>. That's obviously why I so found so many quirks in Monkey C (at least they seemed like quirks to me.)

    There's so many things to think about, like how to handle comparisons/arithmetic on null.... And sometimes it seems like there's no one good answer to every decision you have to make. I decided to say that "5 - null = null" but maybe I should've said that "5 - null = 5", like Javascript. Oh well, too late now.

    So I don't really envy the people who have to make these decisions and are stuck with them forever. I can sort of relate to that, too. (The difference is I have a tiny userbase....)
  • Regarding your new issue. As best I can tell, floats just simply are not allowed to be interpreted as true/false values. You should definitely avoid attempting to do so. Based on what I can see, I would really expect some sort of exception or failure in the case where you attempt to evaluate if( 0.0 ). As far as I can tell, it is not considered to be true or false, so behavior here is probably a bit undefined. Some sort of ticket is probably in order since the system appears to just be ignoring this type of evaluation instead of producing an error.
    ...

    The key to avoiding these types of issues appears to be keeping all your null checking separate from other logic.


    Just to revisit this: if Monkey C suddenly starts throwing an error for "if (x)" where x is a float, couldn't that break existing code in the field? Code where x was never or rarely 0.0?

    I realize it's sort of a no-win situation, but maybe this highlights that this is the kind of thing that should be documented, so existing code can be audited and new code can be written properly.

    Furthermore, in a duck-typed language, shouldn't the goal be to have as few "type errors" as possible? If I'm allowed to use types "interchangeably", it would be nice to not have to think about types all the time.

    My preference would be to just fix the "if (0.0)" case and allow interpreting floats as true or false.
  • One thing to consider here is even if they change how this works in a new SDK/VM, you will have a legacy issue - devices that rarely get new FW/VM. The va and vahr just got new FW for the first time in many, many months, and even for newer devices, the APAC FW can lag the ROW firmware by weeks to months.

    So even if this is fixed in a new VM, you'll likely want to code such that it's not fixed.
  • jim_m_58 agreed. I target most devices in my apps, so it's def a problem I've had before (not being able to take advantage of a a fix). Sometimes it's frustrating that the latest fixes and APIs are only really useful if you target newer devices. This is why I have a ton of conditional code in all my apps....

    I suppose looking to the future (2-3 years) nobody will be using CIQ1/2 devices and this won't be a problem. Or will they? I know people who still use a 220 or a 910XT....

    Having said that, the issue of whether or not a fix will be available for all devices is separate from the issue of what that fix should be....
  • In some cases Monkey C is trying to be everything. In some cases it makes things easy because it just works the way you expected it to based on that other thing you know, but in other cases, it works like that other thing you don't know.


    LOL

    Monkey C is not only a duck-typed language. It's a duck itself. :-D

  • r.485 Haha! Well, I don't want to second-guess a language design when I prolly couldn't do half as a good a job (just look at AppBuilder >_>), but I get the feeling that a lot of things were taken from either JavaScript or Java, which I personally find confusing since those two languages seem to have completely different design goals IMO.

    For example, the "no implicit conversion from float to boolean" seems to be from Java. But Java also does not allow implicit conversion from integer to boolean, yet Monkey C does. I don't see how integers should be any more "special" than floats in this context. Especially because comparing 0 to 0.0 certainly works (as it should).

    Not to beat a dead horse, but in a duck-typed language, I would expect as many implicit conversions to be allowed as possible. Imagine if the following code produced an exception in JavaScript half the time, depending on the type of x:
    if (x)
    {
    console.log("x is truthy");
    }


    I think that would make it much harder to code in JavaScript. Isn't the goal here to be noob-friendly and promote ease of use?

    And if a certain conversion is not allowed, I would also expect it to be documented.