Why can't non-nullable fields be initialized in a constructor body in Dart? - flutter

Often times I have an instance field that needs to be initialized in a constructor. For example, it might be needed to be calculated based on other instance fields, hence I can't initialize it inline (where declared) or with a constructor initializer list.
But it either has to be nullable, or declared late, if I need to initialize it in constructor. The rationale behind late keyword is that programmer states "I'll initialize this before using, trust me", when it can not be determined by the compiler that initialization will take place before first usage. BUT: this "programmer guarantee" seems A) terrible and B) unnecessary in case of constructors, because it can be determined by compiler whether the field was initialized in a constructor (and constructor itself is obviously guaranteed to execute before any other instance methods).
Obvious downside to using late fields in such scenarios is that nothing enforces them compile-time to be actually initialized during construction (or anywhere, for that matter). Plus, every time the late field is read, a runtime check is inserted to make sure it has been assigned a value - I don't need that when I initialize in constructors.
Therefore, it seems that, technically it should be possible to have non-nullable non-late fields that are initialized within a constructor body (and if they are not - compiler can throw an error).
So what is the rationale of requiring constructor-initialized fields to be either nullable, or declared as late? Is there a technical reason why this limitation is imposed, or is it just a design oversight by the Dart team?

Dart executes constructor bodies inside-out, from base class to derived class. This allows virtual dispatch to occur in the constructor body. The fact that virtual dispatch can occur in the constructor body means that the compiler cannot statically determine what code will be executed by the constructor body, and therefore it cannot deduce what the constructor body might ultimately initialize.
That the constructor body can execute arbitrary code (including callbacks) that might initialize members or that might depend on initialized members makes it even more complicated.
Furthermore, allowing members to be not initialized when the constructor body runs would be error-prone and a source for confusion. For example, with:
class SomeClass {
int member;
SomeClass() {
updateMember(0);
}
void updateMember(int value) {
print(value); // Oops.
member = value;
}
}
With Dart's current design, all instance methods (and their overrides) can be guaranteed that members are initialized when the method is called. If members were allowed to be uninitialized when the constructor body is executed, that would not longer be true, and all instance methods then would need to consider if they might be invoked from the constructor (or from a base class constructor), possibly indirectly from other method calls, and whether accessed members might not be initialized yet.
(I'll grant that that previous point isn't terribly strong since it currently can still happen that a member is initialized to an object that the constructor body must mutate, but typically instance methods receiving an empty List, Map, etc. is less of a problem than receiving uninitialized members. The above situation also could happen with late members, but that's the baggage that comes with choosing to use late.)
Null-safety disallows the possibility of accessing uninitialized non-late variables, but your proposal would make that possible.
Also, because there is a distinction between initializing members via an initializer list and via a constructor body, people are encouraged to use initializer lists when possible.

The point that you are ignoring here is that when you want to pass a variable to a constructor you will definitely initialize it, otherwise you wouldn't be able to use that widget because you have to pass the variables needed to its constructor. so this late or nullable keywords can be used for the values that you are trying to pass to a widget and not in the widget itself that you are passing them to, but before it.

Related

Flutter: Late Initializing a "widget-dot" Class-level Variable in a Stateful Widget -- What's Going on?

Consider the following class level property inside the state class of a stateful widget:
int myInt = widget.int;
Android Studio informs that: "The instance member 'widget' can't be accessed in an initializer."
(I understand what this error means).
So then if we add the late keyword, it appears to be fine:
late int myInt = widget.int;
However... this is surprising to me that I’m allowed to do all that in one line — I thought that late variables had to be not set/set as null:
late int myInt;
... and then assign inside onInit.
Since I didnt declare when to assign it, I dont know when the assignment takes place.
The question is:
Is the one-liner “late int myInt = widget.int;” exactly equivalent to assigning it myself in the initState method?
The late keyword in Dart has 2 distinct usages, melt into a single keyword.
The first usage, e.g. late int i:
This usage is well-known: delay assigning a value until later. This is most commonly used to make a field non-nullable, even though you might not have the value right away. I'm sure you are familiar with this usage.
The second usage, e.g. late int i = 0:
This is to delay the value calculation until the field is being accessed. This is useful when the value is expensive to calculate, so you might want to delay its calculation until it's needed for the first time. It's stated on the official documentation:
When you do this, the initializer becomes lazy. Instead of running it
as soon as the instance is constructed, it is deferred and run lazily
the first time the field is accessed. In other words, it works exactly
like an initializer on a top-level variable or static field. This can
be handy when the initialization expression is costly and may not be
needed.
So basically, depends on whether you assign a value right away (on the same line), Dart will decide which of the 2 usages you are using. If you write late int i it will be the first usage, if you write late int i = 0 or late int i = calculateValue() it will be the second usage: delay the calculation until when the field i is accessed for the first time. It's like lateinit in Kotlin or lazy in Swift.
Now back to your case. By assigning a value on the same line as the late keyword, you are using the second usage, basically "lazy init" until the field is accessed for the first time. By the time it's accessed, this class would've been instantiated, so (by that time) you are allowed to use the this keyword.
In the first case Android studio throws that error because int myInt requires a value the moment you are declaring it.
In that particular moment, in the Statefull widget state, the widget object is not accessible.
In the second case:
late int myInt = widget.int;
That is a valid one line declaration and assignment of the variable, but the effect is a bit different from the onInit alternative.
The late keyword works in a lazy way.
Instead of running as soon as the instance is built, it run the first time the field is used. In that moment the widget object will be accessible.
Take a look at the answer to this question, it can be helpful: here
Assigning the value inside the onInit guarantees that the value is actually assigned only once when the widget is initialized.
widget.xxx corresponds to the value of xxx of the instance of a widget, ie the widget once it exists.
So when you use widget.xxx in initialisation of the widget, the var xxx does not exists.
That's why the dart compiler tell you The instance member 'widget' can't be accessed in an initializer .
By adding the keyword late in front of the declaration, you tell the compiler that this variable will be defined later.
But be careful, it will then really have to be defined later (in initState for example) and in any case before any use.
This error comes from the fact that dart is now a null safety aware language.
That is to say a language that strives to ensure that no variabales can have a null value. This is for reasons of code quality and greater code security.

Should I mark all method parameters as final and specify type

Effective dart specifies that top-level variables should be final when applicable:
https://dart-lang.github.io/linter/lints/prefer_final_fields.html
However, I could not find any information about method parameters.
Flutter repo's code functions are mostly ones with parameters not marked final, that includes all the overridden build methods I've seen.
Which of the following is better in terms of performance and app weight:
#override build(context)
#override build(BuildContext context)
#override build(final BuildContext context)
Perhaps overridden functions should be defined the same way as the super function? Is there any difference between overridden functions like build above which can infer the type, and other named/unnamed functions (other than not setting the type makes the variable dynamic), which Flutter repo also writes this way:
static double _flingDistancePenetration(double t) { // t is not final, although treated as immutable
return (1.2 * t * t * t) - (3.27 * t * t) + (_initialVelocityPenetration * t);
}
I've seen this question about Java: Why would one mark local variables and method parameters as "final" in Java?, and, while I agree with the top answer, I'm completely in the dark about why does Flutter repo not do that.
Regarding parameter types: yes, always include them. Otherwise your parameter is likely to be dynamic1, which incurs runtime overhead and no type safety. You also want your API to clearly specify what argument types it expects.
Regarding final: That is an opinion-based question, and my opinion is that while I agree with the prefer_final_fields lint, I disagree with the prefer_final_locals lint. I think that adding final for local variables (including function/method parameters) is pointless.
Contrary to what the top-voted answer to the related Java question says, any half-decent compiler should be able to easily determine whether a local variable is reassigned. If there's any optimization opportunity there, the compiler should be able to do it for you.
In languages such as Dart where implementation usually is inline with the interface (as opposed to languages such as C or C++, which have separate header files to declare interfaces), adding final to parameters is visual noise. It provides no useful information to callers.
You shouldn't unilaterally mark all function/method parameters as final anyway. Sometimes reassigning parameters is appropriate. For example, if your function needs to perform some sort of normalization on its arguments:
void foo(File file) {
file = file.absolute;
...
}
in such a case, using a final File file would mean you'd need a separate local variable for the normalized version, and now the code would be error-prone since it could accidentally use the original variable.
final for a field is an important part of an API. It tells callers that the field will not be reassigned; the object referred to by the field will always be the same object. final for local variables and for function/method parameters affects only the person implementing the function. If the implementor doesn't want to reassign a variable, they can choose to simply not reassign it.
Some people will claim that having final local variables helps code readability since they know that the variable will not be reassigned. However, I instead think that final can be misleading since it says only that a variable cannot be reassigned, not that it won't be mutated, and knowing the latter is much more important.
1 For overridden methods, parameter types are constrained by the corresponding parameter types declared by the base class. Therefore omitting a parameter type in an overridden method will use the parameter type from the base class.
Answer to the author of the question.
You should not mark all method parameters as final
You should specify the type
Examples:
foo(Baz baz)
void main(List<String> args)
An exception to these rules.
You can omit the type indication if the type is dynamic
You can don't specify type if the type can be inferred automatically by the compiler, in the case of using an function-expression.
foo(Baz baz, bar)
list.sort((x, y) => x.compareTo(y))
Some useful information:
From Dart specification:
Dart Programming Language Specification
5th edition draft
Version 2.2
A final variable is a variable whose binding is fixed upon initialization; a final variable v will always refer to the same object after v has been initialized. A variable is final iff its declaration includes the modifier final or the modifier const. A mutable variable is a variable which is not final.
Variable immutability does not make an object immutable in Dart.
This is a very common misconception when variable immutability is confused with object immutability.
P.S.
A common misconception is that Dart users get the impression that function parameters in Dart are passed by reference.
This is not true.
Parameters are passed by value (not by reference).
But due to the fact that (as indicated in the specification) any variable already contains a reference (refers) to the object (but not the object itself), in fact, this reference is passed, by value (as normal value).
That is, a value is passed, but the value itself is of the Referernce type, which is indicated in the language specification.
From Dart specification:
a final variable v will always refer to the same object
That is, the variable stores the reference, but not the value itself.
It is this reference that is passed by value, because there is no reason to pass this reference by reference.

Why is 'init' not assignable?

I just read that the init method can't be used as a value. Meaning:
var x = SomeClass.someClassFunction // ok
var y = SomeClass.init // error
Example found on Language reference
Why should it be like that? Is it a way to enforce language level that too dirty tricks come into place, because of some cohertion or maybe because it interferes with another feature?
Unlike Obj-C, where the init function can be called multiple times without problems, in Swift there actually is no method called init.
init is just a keyword meaning "the following is a constructor". The constructor is called always via MyClass() during the creation of a new instance. It's never called separately as a method myInstance.init(). You can't get a reference to the underlying function because it would be impossible to call it.
This is also connected with the fact that constructors cannot be inherited. Code
var y = SomeClass.init
would also break subtyping because the subtypes are not required to have the same initializers.
Why should it be like that?
init is a special member, not a regular method.
Beyond that, there's no reason that you'd ever need to store init in a variable. The only objects that could use that function in a valid way are instances of the class where that particular init is defined, and any such object will have already been initialized before you could possibly make the assignment.
Initializers don't have a return value. In order to assign it to something, it should be able to return something - and it doesn't.

For an object in scala to be immutable what is required?

Is the answer to this :
Instantiation of the object uses 'val' instead of 'var'.
Each member variable of the object being created is also 'val' instead of 'var'. This is to prevent users updating an object value after its set.
An object is immutable if there is no way for the user of that object to mutate it. This means that it must have no public methods that reassign any of its member variables or mutate any objects referred to by those variables. If all the object's members are vals this ensures the former (i.e. they can't be reassigned), but not the latter (i.e. if the objects referred to by those variables are themselves mutable, they can still be mutated by calling mutating methods on them even if they're referred to only by vals).
Also note that even if the members are declared as vars, the object can still be immutable if none of the object's methods actually reassign the variables (or call mutating methods on them) - assuming of course, they're private.
So having only val members is neither necessary nor sufficient for an object being immutable. Whether the object is referred to by a val or a var (or both) makes no difference in that matter.
#sepp2k nicely and correctly explains the criteria for an object being technically immutable. One subtle point missing from his answer is that not all member variables correspond to externally visible state. A member may also be e.g. a cached internal value to store some local, hard to compute data which is not directly visible from outside (thus qualified as private[this] in Scala). An object can have such a var member e.g. to store a computed hash value. It can even be accessible via a public getter - as long as the behaviour of the accessor is purely functional, i.e. it always produces the same value for each invocation on the same object (except that it returns faster when reusing the internally cached value).
The Scala compiler is aware of this distinction so it can help one to implement an immutable class correctly, even when using mutable state internally. This is important when generic type variance comes into play. Namely, the compiler allows a generic type parameter to be covariant even if the class contains reassignable fields of this type - as long as these fields are private[this], ensuring that one cannot have a reference to a containing object that has a statically weaker type than the type the object was defined with (which would be a precondition for variance to cause type errors).
This is explained in more detail, with a code example, in section 19.7 of Programming in Scala.

Scala instance value scoping

Note that this question and similar ones have been asked before, such as in Forward References - why does this code compile?, but I found the answers to still leave some questions open, so I'm having another go at this issue.
Within methods and functions, the effect of the val keyword appears to be lexical, i.e.
def foo {
println(bar)
val bar = 42
}
yielding
error: forward reference extends over definition of value bar
However, within classes, the scoping rules of val seem to change:
object Foo {
def foo = bar
println(bar)
val bar = 42
}
Not only does this compile, but also the println in the constructor will yield 0 as its output, while calling foo after the instance is fully constructed will result in the expected value 42.
So it appears to be possible for methods to forward-reference instance values, which will, eventually, be initialised before the method can be called (unless, of course, you're calling it from the constructor), and for statements within the constructor to forward-reference values in the same way, accessing them before they've been initialised, resulting in a silly arbitrary value.
From this, a couple of questions arise:
Why does val use its lexical compile-time effect within constructors?
Given that a constructor is really just a method, this seems rather inconsistent to entirely drop val's compile-time effect, giving it its usual run-time effect only.
Why does val, effectively, lose its effect of declaring an immutable value?
Accessing the value at different times may result in different results. To me, it very much seems like a compiler implementation detail leaking out.
What might legitimate usecases for this look like?
I'm having a hard time coming up with an example that absolutely requires the current semantics of val within constructors and wouldn't easily be implementable with a proper, lexical val, possibly in combination with lazy.
How would one work around this behaviour of val, getting back all the guarantees one is used to from using it within other methods?
One could, presumably, declare all instance vals to be lazy in order to get back to a val being immutable and yielding the same result no matter how they are accessed and to make the compile-time effect as observed within regular methods less relevant, but that seems like quite an awful hack to me for this sort of thing.
Given that this behaviour unlikely to ever change within the actual language, would a compiler plugin be the right place to fix this issue, or is it possible to implement a val-alike keyword with, for someone who just spent an hour debugging an issue caused by this oddity, more sensible semantics within the language?
Only a partial answer:
Given that a constructor is really just a method ...
It isn't.
It doesn't return a result and doesn't declare a return type (or doesn't have a name)
It can't be called again for an object of said class like "foo".new ("bar")
You can't hide it from an derived class
You have to call them with 'new'
Their name is fixed by the name of the class
Ctors look a little like methods from the syntax, they take parameters and have a body, but that's about all.
Why does val, effectively, lose its effect of declaring an immutable value?
It doesn't. You have to take an elementary type which can't be null to get this illusion - with Objects, it looks different:
object Foo {
def foo = bar
println (bar.mkString)
val bar = List(42)
}
// Exiting paste mode, now interpreting.
defined module Foo
scala> val foo=Foo
java.lang.NullPointerException
You can't change a val 2 times, you can't give it a different value than null or 0, you can't change it back, and a different value is only possible for the elementary types. So that's far away from being a variable - it's a - maybe uninitialized - final value.
What might legitimate usecases for this look like?
I guess working in the REPL with interactive feedback. You execute code without an explicit wrapping object or class. To get this instant feedback, it can't be waited until the (implicit) object gets its closing }. Therefore the class/object isn't read in a two-pass fashion where firstly all declarations and initialisations are performed.
How would one work around this behaviour of val, getting back all the guarantees one is used to from using it within other methods?
Don't read attributes in the Ctor, like you don't read attributes in Java, which might get overwritten in subclasses.
update
Similar problems can occur in Java. A direct access to an uninitialized, final attribute is prevented by the compiler, but if you call it via another method:
public class FinalCheck
{
final int foo;
public FinalCheck ()
{
// does not compile:
// variable foo might not have been initialized
// System.out.println (foo);
// Does compile -
bar ();
foo = 42;
System.out.println (foo);
}
public void bar () {
System.out.println (foo);
}
public static void main (String args[])
{
new FinalCheck ();
}
}
... you see two values for foo.
0
42
I don't want to excuse this behaviour, and I agree, that it would be nice, if the compiler could warn consequently - in Java and Scala.
So it appears to be possible for methods to forward-reference instance
values, which will, eventually, be initialised before the method can
be called (unless, of course, you're calling it from the constructor),
and for statements within the constructor to forward-reference values
in the same way, accessing them before they've been initialised,
resulting in a silly arbitrary value.
A constructor is a constructor. You are constructing the object. All of its fields are initialized by JVM (basically, zeroed), and then the constructor fills in whatever fields needs filling in.
Why does val use its lexical compile-time effect within constructors?
Given that a constructor is really just a method, this seems rather
inconsistent to entirely drop val's compile-time effect, giving it its
usual run-time effect only.
I have no idea what you are saying or asking here, but a constructor is not a method.
Why does val, effectively, lose its effect of declaring an immutable value?
Accessing the value at different times may result in different
results. To me, it very much seems like a compiler implementation
detail leaking out.
It doesn't. If you try to modify bar from the constructor, you'll see it is not possible. Accessing the value at different times in the constructor may result in different results, of course.
You are constructing the object: it starts not constructed, and ends constructed. For it not to change it would have to start out with its final value, but how can it do that without someone assigning that value?
Guess who does that? The constructor.
What might legitimate usecases for this look like?
I'm having a hard time coming up with an example that absolutely
requires the current semantics of val within constructors and wouldn't
easily be implementable with a proper, lexical val, possibly in
combination with lazy.
There's no use case for accessing the val before its value has been filled in. It's just impossible to find out whether it has been initialized or not. For example:
class Foo {
println(bar)
val bar = 10
}
Do you think the compiler can guarantee it has not been initialized? Well, then open the REPL, put in the above class, and then this:
class Bar extends { override val bar = 42 } with Foo
new Bar
And see that bar was initialized when printed.
How would one work around this behaviour of val, getting back all the
guarantees one is used to from using it within other methods?
Declare your vals before using them. But note that constuctor is not a method. When you do:
println(bar)
inside a constructor, you are writing:
println(this.bar)
And this, the object of the class you are writing a constructor for, has a bar getter, so it is called.
When you do the same thing on a method where bar is a definition, there's no this with a bar getter.