Make initializers and extension methods constant

Issue #5 on hold
Luca Cras created an issue

I do not know whether there is any downside or difficulty connected to implementing this feature, but it would be really helpful, in our opinion, if we can construct instances of fling units as constants such as 0.seconds or milli.grams(15) etc. right now they are not constant (and can therefore not be used as default values in function definitions, etc.)

Comments (4)

  1. Bruce Santier repo owner

    Thanks Luca!

    Believe it or not, I did try to keep things as const-able as I could. Unfortunately, not everything is currently set up that way, as you noticed. I can say that Fling currently supports const versions of the zero-valued measurements, e.g.:

    // Measurement.zero() is const:
    const defaultTime = Time.zero();
    
    // or, if you want a specific unit as the default unit for the measurement:
    const defaultTimeInMinutes = Time.zero(minutes);
    

    Infinite and negative infinite values (via infinite() and negativeInfinite()) are also const, though I suspect those are less useful.

    I suspect there are several reasons I was forced to go non-const for the other initializers. Taking a quick look, the first thing that breaks are the Precision values: I wanted their initializers to check for negative numbers and throw, but that is a runtime check and thus not allowed in const constructors.

    I do understand your desire for a simple, const capable value, so I’ll see what I can work out. Stay tuned.

  2. Bruce Santier repo owner

    It is time for an update based on a little research I’ve done:

    The biggest difficulty is that a const object cannot perform any work during initialization, and cannot be the result of any function calls (i.e. the result of any function call cannot be const). This affects three parts of Fling Units:

    • Extensions: These are methods by definition. Because method calls cannot return const values, I cannot find a way to make something like 5.minutes be const.
    • Prefixes: I have implemented these as method calls. Therefore, they suffer from the same problem. This affects things like meters(5) or milli.grams(3). I also cannot find a way to make these be const.
    • Measurement Constructors: In order to remain in a “known” state, I do some work when you initialize a Measurement. In short, I convert from whatever unit you are using to the SI base unit. Since this involves a method call to the Interpreter, the constructor cannot be made const.

    So, extensions and prefixes are out.

    Really, the only way to make a Measurement const (that I know of) is to accept an unconverted measurement amount in the constructor. I am able to do this already for some quantities, namely 0 and infinity/-infinity, since there is no need to convert those values. In order to allow this for all other quantities, I could instead perform the conversions on-demand. This is doable, but requires quite a bit of refactoring. In the end, you would be able to create const instances of Measurement classes like this:

    var myDistance = const Distance(30.0, inches);
    

    I know it’s not as pretty as what the extension methods allow, but it would get us close to what we want. I say “close” because the prefixes would not work here, since they are methods, so this would still not work:

    var myDistance = const Distance(30.0, milli.meters); // still refuses to compile!
    

    For that, I would need to turn all of the prefixes into classes with const constructors. That would allow something like this:

    var myDistance = const Distance(30.0, Milli(meters));
    

    One last thing I could do would be to define constants for all of the possible combinations of prefixes and units (or at least the most common ones - not sure how often people use milli.feet in their measurements), getting us closest to what you might expect:

    var myDistance = const Distance(30.0, millimeters);
    

    OK, now the conclusion:

    As I mentioned, doing something like this would be a significant endeavor. I might run into other problems I have not foreseen partway through, as well. I also don’t like the partial solutions since they deviate from the standard (e.g. having to use Milli(meters) or millimeters rather than the standard milli.meters). Even then, testing and maintaining the combinatorial explosion of constants becomes more difficult and increases the footprint of the library. In short, I’d prefer to skip this feature for now.

    If you want to support default values in methods, I might suggest something like the following:

    void doIt([Distance? myDistance]) {
      myDistance = myDistance ?? 50.milli.meters;
      // do stuff with myDistance
    }
    

    For constructors, you can do something similar, but you won’t be able to make your constructor const:

    class DoIt {
      final Distance myDistance;
      DoIt([Distance? myDistance]) : myDistance = myDistance ?? 50.milli.meters;
    }
    

    Dart is still evolving, and some things that were not possible before are becoming possible now (e.g. extensions!). It’s also possible that I have missed a new Dart feature that solves this problem, or at least makes it much easier to solve. If you have ideas, please let me know. I’ll keep an eye out as well, and I’ll keep this as a high priority for version 3.0

  3. Log in to comment