As mentioned before, I currently do not validate that non-null fields have been initialized to a non-null value upon construction.
I am ok with this design because I think forcing you set the field to something will create a lot of busy work to just to please the compiler because often the life cycle of a given instance is more complex than can be captured completely in a constructor. You might think of this case as a field which is should never be set to null, but might be null on get if the object hasn't finished initialization.
My proposal is to keep the existing design. If you have comments otherwise, let's discuss.
tompalmerMon 26 Jan 2009
I tend to agree with the current design.
jodastephenTue 27 Jan 2009
My simple rule is that reading the code must not lie. If a field says it is non-null, then it must never return null, otherwise all trust in the null typing is lost.
The exception is during the construction process, but not beyond that point. ie. a field can return null during construction, but must be set by the end of the full construction process.
andyTue 27 Jan 2009
+1 Stephen - I think this is a must fix.
brianTue 27 Jan 2009
The problem is that this is really tough to solve without something like a Maybe. The only thing you can do in most cases is change to a nullable type. So the side effect of implementing this change is that many more fields will have to declared nullable (even though you should never assign null). If there was a clean way to capture that in the type system that would slick, but field setters don't really have a formal signature.
brianTue 27 Jan 2009
I've it some more thought. I am not sure that checking for null is the right design, but it is more restrictive than not-checking. So better to be strict now and relax the rules later.
The way I will implement this is something similar to how definite assignment works with Java final fields. The compiler will require either a field default expr or an assignment in each of the constructors along all the code paths. Which means you can't rely on client side code to set in a with-block, everything must be initialized in ctor (have to see how that works out). This means that this is largely just a compile time check, not a runtime check.
JohnDGWed 28 Jan 2009
The problem is that this is really tough to solve without something like a Maybe.
If you have Maybe you don't need null. In any case, I think this problem boils down to programming practice. The current behavior of Fan is sloppy and formally incorrect, but sometimes convenient -- i.e. saves more lines of code than doing things in a formally correct way (see my example of Expr versus TypedExpr).
When I'm programming in Java, I seldom use anything but final fields, which must be initialized in the constructor. There is no design that cannot be expressed elegantly, cleanly, and safely with this approach, although in some cases it requires more lines of code (even though the complexity of those lines is lower, and thus maintenance is easier).
andyThu 29 Jan 2009
Which means you can't rely on client side code to set in a with-block
Yeah that sort of sucks and reduces the usefulness of with-blocks in that context. But I guess you're right, better to be strict now and relax later. Though I have a feeling we won't like, not that I have a better suggestion at the moment though.
JohnDGThu 29 Jan 2009
Which means you can't rely on client side code to set in a with-block, everything must be initialized in ctor (have to see how that works out).
Why can't you let it happen in a with block, too? i.e. issue a compiler complaint if some code constructs an object but does not initialize a field in either the constructor or the constructing with block.
brianThu 29 Jan 2009
The problem with doing the checking after the with-block is that instead of localizing the checks inside the constructor, you have to generate an extra method call after every single use of the constructor. It can be done, but it is a lot of runtime overhead versus a single compile time check.
JohnDGThu 29 Jan 2009
It can be done, but it is a lot of runtime overhead versus a single compile time check.
I don't mind. Generally when people choose performance over a particular useful (if slower) design choice, they regret it, as performance increases but design choices impact code forever. And Fan might use an extra method now, but always? Maybe not.
brianFri 30 Jan 2009
In this case it is really about starting strict and relaxing the rules later. The strictest rule (and easiest to understand and check) is that upon completion of the constructor that all non-null fields have been assigned a non-null value.
alexlamslFri 30 Jan 2009
I do find it useful to have uninitialised final fields which means clients would have to assign to it in a with-block.
It is analoguous to parameters of a constructor with absence of no-arg constructor - forcing the API user to supply a value in order to initialise the Obj properly.
Having said that, the current evolutionary path does allow for this to happen, so no complaints here :-)
brian Mon 26 Jan 2009
As mentioned before, I currently do not validate that non-null fields have been initialized to a non-null value upon construction.
I am ok with this design because I think forcing you set the field to something will create a lot of busy work to just to please the compiler because often the life cycle of a given instance is more complex than can be captured completely in a constructor. You might think of this case as a field which is should never be set to null, but might be null on get if the object hasn't finished initialization.
My proposal is to keep the existing design. If you have comments otherwise, let's discuss.
tompalmer Mon 26 Jan 2009
I tend to agree with the current design.
jodastephen Tue 27 Jan 2009
My simple rule is that reading the code must not lie. If a field says it is non-null, then it must never return null, otherwise all trust in the null typing is lost.
The exception is during the construction process, but not beyond that point. ie. a field can return null during construction, but must be set by the end of the full construction process.
andy Tue 27 Jan 2009
+1 Stephen - I think this is a must fix.
brian Tue 27 Jan 2009
The problem is that this is really tough to solve without something like a Maybe. The only thing you can do in most cases is change to a nullable type. So the side effect of implementing this change is that many more fields will have to declared nullable (even though you should never assign null). If there was a clean way to capture that in the type system that would slick, but field setters don't really have a formal signature.
brian Tue 27 Jan 2009
I've it some more thought. I am not sure that checking for null is the right design, but it is more restrictive than not-checking. So better to be strict now and relax the rules later.
The way I will implement this is something similar to how definite assignment works with Java final fields. The compiler will require either a field default expr or an assignment in each of the constructors along all the code paths. Which means you can't rely on client side code to set in a with-block, everything must be initialized in ctor (have to see how that works out). This means that this is largely just a compile time check, not a runtime check.
JohnDG Wed 28 Jan 2009
If you have
Maybe
you don't neednull
. In any case, I think this problem boils down to programming practice. The current behavior of Fan is sloppy and formally incorrect, but sometimes convenient -- i.e. saves more lines of code than doing things in a formally correct way (see my example ofExpr
versusTypedExpr
).When I'm programming in Java, I seldom use anything but
final
fields, which must be initialized in the constructor. There is no design that cannot be expressed elegantly, cleanly, and safely with this approach, although in some cases it requires more lines of code (even though the complexity of those lines is lower, and thus maintenance is easier).andy Thu 29 Jan 2009
Yeah that sort of sucks and reduces the usefulness of with-blocks in that context. But I guess you're right, better to be strict now and relax later. Though I have a feeling we won't like, not that I have a better suggestion at the moment though.
JohnDG Thu 29 Jan 2009
Why can't you let it happen in a
with
block, too? i.e. issue a compiler complaint if some code constructs an object but does not initialize a field in either the constructor or the constructingwith
block.brian Thu 29 Jan 2009
The problem with doing the checking after the with-block is that instead of localizing the checks inside the constructor, you have to generate an extra method call after every single use of the constructor. It can be done, but it is a lot of runtime overhead versus a single compile time check.
JohnDG Thu 29 Jan 2009
I don't mind. Generally when people choose performance over a particular useful (if slower) design choice, they regret it, as performance increases but design choices impact code forever. And Fan might use an extra method now, but always? Maybe not.
brian Fri 30 Jan 2009
In this case it is really about starting strict and relaxing the rules later. The strictest rule (and easiest to understand and check) is that upon completion of the constructor that all non-null fields have been assigned a non-null value.
alexlamsl Fri 30 Jan 2009
I do find it useful to have uninitialised
final
fields which means clients would have to assign to it in a with-block.It is analoguous to parameters of a constructor with absence of no-arg constructor - forcing the API user to supply a value in order to initialise the
Obj
properly.Having said that, the current evolutionary path does allow for this to happen, so no complaints here :-)