Global functions. Good Practice?

I have "discovered' the ability in MC to build functions outside a class and to call them from within a class.
Obviously they can't access any class variables, which I overcome by reading Storage values (very sparingly) , and that's OK as I'm using then to manage views.
The upside is that the code is simpler as I can call them directly from anywhere in my code.

My question is: Is this "good practice" and if not why not?

  • Don't forget you can access globals directly using $.variable to avoid scope delays. I normally do this when using globals.


    A little remark: nothing comes for free. Using "$." increases memory usage. The code I used to test is below.

    (@garmin: maybe some compiler optimisation could help?)

    using Toybox.Application;
    using Toybox.WatchUi;

    var globalVar01;
    var globalVar02;
    var globalVar03;
    var globalVar04;
    var globalVar05;
    var globalVar06;
    var globalVar07;
    var globalVar08;
    var globalVar09;
    var globalVar10;
    var globalVar11;
    var globalVar12;
    var globalVar13;
    var globalVar14;
    var globalVar15;
    var globalVar16;
    var globalVar17;
    var globalVar18;
    var globalVar19;
    var globalVar20;
    var globalVar21;
    var globalVar22;
    var globalVar23;
    var globalVar24;
    var globalVar25;
    var globalVar26;
    var globalVar27;
    var globalVar28;
    var globalVar29;
    var globalVar30;
    var globalVar31;
    var globalVar32;
    var globalVar33;
    var globalVar34;
    var globalVar35;
    var globalVar36;
    var globalVar37;
    var globalVar38;
    var globalVar39;
    var globalVar40;
    var globalVar41;
    var globalVar42;
    var globalVar43;
    var globalVar44;
    var globalVar45;
    var globalVar46;
    var globalVar47;
    var globalVar48;
    var globalVar49;
    var globalVar50;
    var globalVar51;
    var globalVar52;
    var globalVar53;
    var globalVar54;
    var globalVar55;
    var globalVar56;
    var globalVar57;
    var globalVar58;
    var globalVar59;
    var globalVar60;

    class GlobalVarsApp extends Application.AppBase {

    function initialize() {
    AppBase.initialize();
    }

    function onStart(state) {
    }

    function onStop(state) {
    }

    function getInitialView() {
    return [ new GlobalVarsView() ];
    }
    }


    class GlobalVarsView extends WatchUi.View {

    function initialize() {
    View.initialize();
    }

    function onLayout(dc) {
    }

    function onShow() {
    }

    function onUpdate(dc) {
    View.onUpdate(dc);

    /*

    globalVar01 = 1;
    globalVar02 = 1;
    globalVar03 = 1;
    globalVar04 = 1;
    globalVar05 = 1;
    globalVar06 = 1;
    globalVar07 = 1;
    globalVar08 = 1;
    globalVar09 = 1;
    globalVar10 = 1;
    globalVar11 = 1;
    globalVar12 = 1;
    globalVar13 = 1;
    globalVar14 = 1;
    globalVar15 = 1;
    globalVar16 = 1;
    globalVar17 = 1;
    globalVar18 = 1;
    globalVar19 = 1;
    globalVar20 = 1;
    globalVar21 = 1;
    globalVar22 = 1;
    globalVar23 = 1;
    globalVar24 = 1;
    globalVar25 = 1;
    globalVar26 = 1;
    globalVar27 = 1;
    globalVar28 = 1;
    globalVar29 = 1;
    globalVar30 = 1;
    globalVar31 = 1;
    globalVar32 = 1;
    globalVar33 = 1;
    globalVar34 = 1;
    globalVar35 = 1;
    globalVar36 = 1;
    globalVar37 = 1;
    globalVar38 = 1;
    globalVar39 = 1;
    globalVar40 = 1;
    globalVar41 = 1;
    globalVar42 = 1;
    globalVar43 = 1;
    globalVar44 = 1;
    globalVar45 = 1;
    globalVar46 = 1;
    globalVar47 = 1;
    globalVar48 = 1;
    globalVar49 = 1;
    globalVar50 = 1;
    globalVar51 = 1;
    globalVar52 = 1;
    globalVar53 = 1;
    globalVar54 = 1;
    globalVar55 = 1;
    globalVar56 = 1;
    globalVar57 = 1;
    globalVar58 = 1;
    globalVar59 = 1;
    globalVar60 = 1;
    */

    $.globalVar01 = 1;
    $.globalVar02 = 1;
    $.globalVar03 = 1;
    $.globalVar04 = 1;
    $.globalVar05 = 1;
    $.globalVar06 = 1;
    $.globalVar07 = 1;
    $.globalVar08 = 1;
    $.globalVar09 = 1;
    $.globalVar10 = 1;
    $.globalVar11 = 1;
    $.globalVar12 = 1;
    $.globalVar13 = 1;
    $.globalVar14 = 1;
    $.globalVar15 = 1;
    $.globalVar16 = 1;
    $.globalVar17 = 1;
    $.globalVar18 = 1;
    $.globalVar19 = 1;
    $.globalVar20 = 1;
    $.globalVar21 = 1;
    $.globalVar22 = 1;
    $.globalVar23 = 1;
    $.globalVar24 = 1;
    $.globalVar25 = 1;
    $.globalVar26 = 1;
    $.globalVar27 = 1;
    $.globalVar28 = 1;
    $.globalVar29 = 1;
    $.globalVar30 = 1;
    $.globalVar31 = 1;
    $.globalVar32 = 1;
    $.globalVar33 = 1;
    $.globalVar34 = 1;
    $.globalVar35 = 1;
    $.globalVar36 = 1;
    $.globalVar37 = 1;
    $.globalVar38 = 1;
    $.globalVar39 = 1;
    $.globalVar40 = 1;
    $.globalVar41 = 1;
    $.globalVar42 = 1;
    $.globalVar43 = 1;
    $.globalVar44 = 1;
    $.globalVar45 = 1;
    $.globalVar46 = 1;
    $.globalVar47 = 1;
    $.globalVar48 = 1;
    $.globalVar49 = 1;
    $.globalVar50 = 1;
    $.globalVar51 = 1;
    $.globalVar52 = 1;
    $.globalVar53 = 1;
    $.globalVar54 = 1;
    $.globalVar55 = 1;
    $.globalVar56 = 1;
    $.globalVar57 = 1;
    $.globalVar58 = 1;
    $.globalVar59 = 1;
    $.globalVar60 = 1;
    }

    function onHide() {
    }
    }
  • Yes r.485, I've seen that. A while back, I took one of my apps and did nothing but a change it to use the $ for globals, and there was a noticeable increase in memory. Since then, I've been avoiding globals, as you pay one way or the other..
  • Yes. This is not a huge surprise if you look at the assembly generated when building with -g.

    BUILD: # globalVar=1;
    BUILD: lgetv 0
    BUILD: spush globalVar
    BUILD: ipush 1
    BUILD: putv
    BUILD: # $.globalVar=1;
    BUILD: spush globals
    BUILD: getm
    BUILD: spush globalVar
    BUILD: ipush 1
    BUILD: putv


    One more instruction is generated when you explicitly qualify the module. I'm a little surprised the difference is as big as it is (I'm seeing ~1KB for 256 such assignments), but I'm not surprised it is more expensive. The thing that sucks you pay this cost any time you explicitly qualify a reference to a function or variable. i.e, referencing WatchUi.TEXT_JUSTIFY_CENTER does this exact same thing...

    BUILD: # varx=Graphics.TEXT_JUSTIFY_CENTER;
    BUILD: spush Toybox_Graphics
    BUILD: getm
    BUILD: spush TEXT_JUSTIFY_CENTER
    BUILD: getv
    BUILD: lputv 1


    I think we could generate a new instruction that combines spush and getm and get most of that back.
  • Would be great, but I have been seduced away from M$ to Mac. Any chance of the code to port across to Mac/Unix?


    I could do this when I have some more time (and access to a Mac or Linux computer for a while)...
    ...but I fear, this won't be within the next months.

    Jut another point about memory usage - which maybe has been observed already by everyone - the variable and constant name lengths do influence the resulting prg files (also when compiled as a release), so I try to avoid names like 'totaldistance' (even it is much clearer than something like 'td')...
  • One more instruction is generated when you explicitly qualify the module. I'm a little surprised the difference is as big as it is (I'm seeing ~1KB for 256 such assignments), but I'm not surprised it is more expensive. The thing that sucks you pay this cost any time you explicitly qualify a reference to a function or variable. i.e, referencing WatchUi.TEXT_JUSTIFY_CENTER does this exact same thing...

    BUILD: # varx=Graphics.TEXT_JUSTIFY_CENTER;
    BUILD: spush Toybox_Graphics
    BUILD: getm
    BUILD: spush TEXT_JUSTIFY_CENTER
    BUILD: getv
    BUILD: lputv 1


    I think we could generate a new instruction that combines spush and getm and get most of that back.


    Interesting that Graphics.TEXT_JUSTIFY_CENTER isn't just replaced with a constant by the compiler.

    It would be interesting, but tedious, to understand what really is getting generated by most of our code. For those that are trying to eek out every last byte on memory limited devices, it could pay big dividends though.

    I'm guessing there isn't any public documentation about the assembly language for monkey C.
  • Jut another point about memory usage - which maybe has been observed already by everyone - the variable and constant name lengths do influence the resulting prg files (also when compiled as a release), so I try to avoid names like 'totaldistance' (even it is much clearer than something like 'td')...

    But if the use of long variable names only affects the size of the prg but doesn't affect the memory use of the app, is a saving in the size of the prg really worth the loss readability of the code? Or is there some other downside to having a larger prg file that I'm missing?
  • ekutter you can use the -g switch to see what is generated, way better than documentation as this info is never out of date ;)
  • Would be great, but I have been seduced away from M$ to Mac.

    The "M$" thing is kinda old and silly in reference to moving to Apple. (The Apple stuff is fine.)