What is the difference between constant named constructor and static constant field below? - dart

What is the difference between a and b below?
class ImmutablePoint {
const ImmutablePoint(this.x, this.y);
final int x;
final int y;
const ImmutablePoint.originA() : this(0, 0);
static const ImmutablePoint originB = const ImmutablePoint(0, 0);
}
void main() {
const a = ImmutablePoint.originA();
const b = ImmutablePoint.originB;
}

The constant variable defines and names a single value.
The const constructor defines a way to create new values.
If that constructor is invoked as const ImmutablePoint.originA() then, because of constant canonicalization, it creates the same constant value that the variable contains. However, you can also call new ImmutablePoint.originA() (with or without the new) and get a new instance which is not identical to any other point object.
A Dart constructor being const means that it can be invoked using const, not that it must.

From a literal perspective, one is a constructor and the other is a static variable. The constructor allows you to define constants, and the static variable already is a constant. That much should be fairly self-explanatory.
From a performance perspective? Both values resolve to the same constant-defined object:
const ImmutablePoint(0, 0);
Thanks to Dart's canonical constant feature, everything that references this constant class with the same values 0,0 (including other calls to ImmutablePoint.originA() or even ImmutablePoint(0, 0)) will be reduced to point to the same compile-time constant. So from a practical point of view, both the parameterless const constructor and the static const variable result in virtually identical compiled code and, therefore, performance.
(Though from a strictly nit-picky point of view, the static const might be compiled to include a static reference to the ImmutablePoint type before referencing the constant. I'm not knowledgeable enough with how Dart compiles these situations to say that for certainty, but I can tell you that even if it does happen, the performance hit for a static type reference will be in the "nanoseconds-or-less" tier of negligible. Don't micro-optimize, just use whatever approach you deem more readable or convenient.)

Related

Putting constant and final in Dart

I was going through the Dart language tour and noticed that they have the statement
final constantSet = const {
'fluorine'
};
and I was just wondering whether there is a programmatic difference in declaring the constant as final or whether that has a specific purpose. I can see that we can place const either before or after the variable declaration, is this the same thing, considering that when you declare a variable as final it is only able to be initialized once anyway?
There is a difference.
A final constantSet = const {'fluorine'}; declares a non-constant final variable bound to a value which happens to be a compile-time constant.
A const constantSet = const {'fluorine'}; declares a constant variable bound to a compile-time constant value (and the second const can be omitted).
The latter allows you to use constantSet in constant expressions, so const [costantSet] is only valid with the latter declaration.
That also means that declaring a variable as const is something you cannot take back. Changing a const declaration to final may break code using the const variable in constant expression. That's a reason for not making every variable const, just because it can be. You might not want to promise that it stays constant forever.

Detecting when a const object is passed to a function that mutates it, in Dart

Take this example:
void modl(List<int> l) {
l.add(90);
print(l);
}
class Foo {
final List<int> bar;
const Foo(this.bar);
#override
String toString() => 'Foo{bar: $bar}';
}
void main() {
var foo = const Foo([1,2,3,4]);
modl(foo.bar);
print (foo);
}
Running the above code results in a runtime Uncaught Error, but removing the const from
var foo = const Foo([1,2,3,4]);
allows it to work.
This seems like a bug to me because the const variable can be mutated and dart detects this at runtime, which means it has the means to detect when a const object is modified, but shouldn't this have been detected at compile time, seeing as const variables are called "compile-time constants".
If this is not a bug, is there anything in dart that allows us to detect at compile time when a const variable will possibly be mutated by an operation?
In C++, the compiler errors out when we try to do something like this. Is there anything we can do in Dart to avoid encountering this error at runtime?
No. Dart const is a compile-time feature around object creation, but it's not reflected in the type system.
You can't tell from the type of any object whether it's a constant or not.
Usually that's not a problem because an instance of a class which can be const is unmodifiable. It's not guaranteed to be deeply immutable, but the instance itself cannot have its fields changed.
Lists, sets and maps can both be either constant and mutable. That's what you are seeing here.
The list argument to const Foo(const [1, 2, 3, 4]) is constant, even if you remove the redundant const on the list literal. You would have the same issue with new Foo(const [1, 2, 3, 4]), which would also provide an immutable foo.bar, but which would otherwise be indistinguishable from new Foo([1, 2, 3, 4]). The only real difference is whether the list is modifiable or not, and the only way to detect that is to try to modify it.
Lists, sets and maps do not provide any way to detect whether they are mutable or not except trying, and catching the error.
When comparing to C++, Dart's notion of being const is a property of the object or, really, the way the object is created. That may have some consequences for the object. A const Foo(..) just creates a normal Foo object, but it can only create deeply immutable objects, and they are canonicalized. A const [...] or const {...} creates a different kind of list/map/set than the non-const literal, but that's not visible in the type.
In C++, being const is a property of an object reference, and it restricts how that reference can be used, but there are no constant objects as such. Any object can be passed as a const reference.
The two concepts are completely different in nature, and just happen to use the same name (and are also both different from JavaScript const).

Const constructor vs. static final canonical values

As per this excellent explanation const expressions in Dart are "deeply immutable" meaning that nothing inside can ever change and therefore the entire expression will always denote the same thing. This is useful for the compiler, because it can generate the entire object graph once and re-use it every time such an expression occurs, and it is useful for the programmer to know that such an expression –even when it is deeply nested– still follows value-semantics and won't do anything behind my back.
I am using those optimizations by the compiler to use a well-structured object model (instead of hand-encoding it in a bit-vector, for example) and still get good performance. Since we can get some of those benefits also by "explicitly hashing" some values by making them run-time constants with the static final idiom, the question arises which of the two is good style to use in which case?
Consider the following example:
enum ShaftType { RING, SUN, CARRIER }
class Shaft {
final int index;
final ShaftType type;
Shaft(this.type, this.index) {
assert((type == ShaftType.CARRIER) == (index == null));
}
const Shaft.CARRIER()
: type = ShaftType.CARRIER,
index = null;
const Shaft.RING(this.index) : type = ShaftType.RING;
const Shaft.SUN(this.index) : type = ShaftType.SUN;
}
class GearPath {
final Shaft input, output, fixed;
GearPath({this.input, this.output, this.fixed}) {
// input and output must be set
assert(null != input && null != output);
// fixed shaft can't be anything else
assert(fixed != input && fixed != output);
}
GearPath.carrierToFirstRingFixedSun(int i)
: input = const Shaft.CARRIER(),
output = const Shaft.RING(0),
fixed = new Shaft.SUN(i) {}
static final singleFixedSunUp = new GearPath(
input: const Shaft.CARRIER(),
output: const Shaft.RING(0),
fixed: const Shaft.SUN(0),
);
static final directDrive = new GearPath(
input: const Shaft.CARRIER(),
output: const Shaft.CARRIER(),
fixed: null,
);
// ...
}
I can't make the main Shaft(..) and GearStage(..) constructors const because I want to check some constraints, but I can provide special-case constructors (such as Shaft.SUN(int i), Shaft.CARRIER()) which comply with those constraints (at least partially) by design and provide users legible shorthands for those common values.
On the other hand, when a const constructor would have no arguments, then I can as well write it as a static final member as I have done with GearStage.directDrive. If all users refer to this static member instead of re-creating the value again, we also get the benefit of sharing memory and fast comparisons (reference to same object). I can't declare the right-side of this definition as const, because it uses the non-const constructor, but developers can see from context that this is indeed a constant value and not global mutable singleton hidden in the static field. So for practical purposes it should be just as good as a const constructor, right?
Since I haven't found this described any where as a best practice my question is simply if this is indeed a good way to combine and trade-off between const constructors and static final "named value instances"?
Finally, I wonder if there is a way to declare GearPath.carrierToFirstRingFixedSun(int i) also as a const constructor? Currently I can't because const Shaft.SUN(i) complains about i not being constant.
(full code of example)
Dart 2 will allow you to have asserts in const constructors (as long as your condition can be computed as a const expression).
Then you will be able to write:
GearPath({this.input, this.output, this.fixed})
: // input and output must be set
assert(null != input && null != output),
// fixed shaft can't be anything else
assert(!identical(fixed, input) && !identical(fixed, output));
Until then, you can't have both validation and const expressions.
You still will not be able to make GearPath.carrierToFirstRingFixedSun(int i) const because it i is not constant. The const Shaft.SUN(i) is still not a valid const expression, even if i is the parameter of a const constructor. Each const Constructor(...) invocation must still create exactly one object, even if it occurs in the initializer list of another const constructor.
As a rule-of-thumb, you should consider whether exposing a value as const instead of final is something you want to commit to. When you make the variable const, it means that someone else can use the value in another const expression, and then changing the variable to final will be a breaking change. Saying that something is const is a commitment that you should choose deliberately, not just because you can. So, consider the use-cases of your variable. If you don't see it being used in other const expressions, then just make it final.

Why does Dart have compile time constants?

Dart has the concept of compile-time constants. A compile-time constant is parsed and created at compile time, and canonicalized.
For example, here is a const constructor for Point:
class Point {
final num x, y;
const Point(this.x, this.y);
}
And here's how you use it:
main() {
var p1 = const Point(0, 0);
var p2 = const Point(0, 0);
print(p1 == p2); // true
print(p1 === p2); // true
}
This is a non-obvious feature, with seemingly no parallels to features in other dynamic languages. There are restrictions on const objects, like all fields must be final and it must have a const constructor.
Why does Dart have compile-time constants?
From the mailing list, Florian Loitsch writes:
The canonicalization property of compile-time constants is nice, but
not the main-reason to have them. The real benefit of compile-time
constants is, that they don't allow arbitrary execution at
construction and can therefore be used at places where we don't want
code to executed. Static variables initializers, for example, were
initially restricted to compile-time constants to avoid execution at
the top-level. In short, they make sure that a program starts with
'main' and not somewhere else.
Lasse's answer here helped me a lot
So, what are compile-time constants good for anyway?
They are useful for enums.
You can use compile-time constant values in switch cases.
They are used as annotations.
Compile-time constants used to be more important before Dart switched
to lazily initializing variables. Before that, you could only declare
an initialized global variable like "var x = foo;" if "foo" was a
compile-time constant. Without that requrirement, most programs can be
written without using any const objects

Mimicking typedef in ActionScript?

I'm working on some ActionScript code that needs to juggle a bunch of similar-but-not-interchangeable types (eg, position-in-pixels, internal-position, row-and-column-position) and I'm trying to come up with a naming scheme to minimize the complexity.
Additionally, I don't yet know what the best format for the "internal position" is – using int, uint and Number all have advantages and disadvantages.
Normally I'd solve this with a typedef:
typedef float pixelPos;
typedef int internalPos;
typedef int rowColPos;
Is there any way of getting similar functionality in ActionScript?
If you're using Flex or another command-line compiler to build your project, you could add a pass from an external preprocessor to your build process.
Doesn't get the type-safety, but otherwise appears to do what you want.
I have found an article titled Typedefs in ActionScript 3, which suggests using:
const pixelPos:Class = int;
But that doesn't work – the compiler complains that "Type was not found or was not a compile-time constant: pixelPos" (note: this also happens when I use Object instead of int).
Here is an example of code which doesn't compile:
const pixelPos:Class = int;
function add3(p:pixelPos):void { // <-- type not found on this line
return p + 3;
}
Just make it static const and you can register your own class. Like this:
static const MyClass:Class = int;
And you can't make a variable with this type:
var ert:MyClass; //error
private function ert2():MyClass {}; //error
But you can make an instance:
var ert:* = new MyClass();

Resources