As the constructor thread is rather long, I'll start this one with a slight tangent. I think its interesting to go back to the existing Fan documentation about with blocks and const fields - they reveal a lot of the ambiguity we are facing here. http://fandev.org/doc/docLang/Expressions.html#withBlocks.
Basically, there are three main uses of with blocks I can see:
(1) with after construction - normal with style blocks that just allow repeatedly calling on the base object
(2) with during construction - allowed to set const fields
(3) with during deserialization - allowed to set const fields; implicit, as you tend not to write this directly, but since someone could manually alter the serialized file it still matters
The key point to notice is how (2) and (3) allow the setting of const fields. They are not just with blocks, but special with blocks. Given how important the setting and validation of const fields are, this isn't an issue we can duck.
One solution would be to prevent constructor with blocks (2) from setting const fields. Unfortunately, this doesn't help, as deserialization (3) still has the same open hole.
Since no-one is challenging that construction with blocks are a Good Thing, they need to work properly and correctly in all cases, including those with const fields.
The implication of all this to me is that (2) and (3) are specifically part of the construction process, and must be validated as such.
The with block after construction (1) is completely different - that simply gets to operate on the public API after successful construction, and is perfectly safe. In fact, it really would be better thought of as a different construct:
p := Point.make(2,4)
// old style
p {
echo(x * y)
}
// new style
using (p) {
echo(x * y)
}
Now we have separated the constructs with the using keyword (or a with keyword). Construction with blocks have no keyword.
This means that this case is now clear:
// allowed in current Fan, but setting xxx has special rules
foo := Bar { xxx = 42 } { yyy = "text" } { zzz = 3.1415926 }
// proposal, clear on special rules
foo := Bar { xxx = 42 } using(foo) { yyy = "text" } using(foo) { zzz = 3.1415926 }
Now, personally I've never found post-construction with blocks (1) to actually be a feature I especially needed. So, maybe they are just a feature to drop altogether?
Anyway, I'm suggesting that treating (1) as the same as (2) and (3) is misleading, and complicating the discussions on constructors.
tompalmerWed 16 Jul 2008
I prefer obj.{/*...*/} style for non-construction "with" blocks, but that's already been discussed before. I'm not sure I want to fight on that subject again. However, I do agree with Brian that post-construction "with" blocks are very nice.
We especially can't say using(obj) {/*...*/} since that exact syntax means something very different in C#.
brianWed 16 Jul 2008
Stephen, all very good points - I think your post helps clarify how with-blocks are used.
It is interesting to note the serialization case, because currently Fan has no mechanism to verify a deserialized object, and that is something I think we'll need. Java doesn't really have this problem since it has a binary serialization syntax, so it is unlikely to be hand coded. Fan serialized objects will often be hand coded.
Now, personally I've never found post-construction with blocks (1) to actually be a feature I especially needed. So, maybe they are just a feature to drop altogether?
I think they are quite useful, because they still allow you to initialize objects created from factories (assuming non-const fields of course). Plus it is extremely common to do set of things to a given object. Couple examples:
I don't know if you've done a lot of Fan programming, but it really does change how you write code.
We especially can't say using(obj) {/*...*/} since that exact syntax means something very different in C#.
Agreed. I am not in favor of using two different syntaxes for with-blocks. I understand where you are coming from Stephen, but it really is just one syntax with different rules. It isn't much different than saying you can set const fields in a constructor but not another method - that is a rule that doesn't require two different assignment operators.
jodastephenWed 16 Jul 2008
Firstly, I'd note that all JDK Java classes are protected against malicious hacking of the binary serialized format. Fan will need the same protection. And that requires validation of the created object after deserialization (and also applying any possible singleton caches).
The problem with one syntax for type (1) and (2) is as follows:
// validation occurs before with-block
Point(4) {y = 6}
// validation occurs after the with-block
Point {x=4;y=6}
brianWed 16 Jul 2008
My basic position is that you need two methods, one before and one after. You can't really do without either one.
jodastephen Tue 15 Jul 2008
As the constructor thread is rather long, I'll start this one with a slight tangent. I think its interesting to go back to the existing Fan documentation about with blocks and const fields - they reveal a lot of the ambiguity we are facing here. http://fandev.org/doc/docLang/Expressions.html#withBlocks.
Basically, there are three main uses of with blocks I can see:
The key point to notice is how (2) and (3) allow the setting of const fields. They are not just with blocks, but special with blocks. Given how important the setting and validation of const fields are, this isn't an issue we can duck.
One solution would be to prevent constructor with blocks (2) from setting const fields. Unfortunately, this doesn't help, as deserialization (3) still has the same open hole.
Since no-one is challenging that construction with blocks are a Good Thing, they need to work properly and correctly in all cases, including those with const fields.
The implication of all this to me is that (2) and (3) are specifically part of the construction process, and must be validated as such.
The with block after construction (1) is completely different - that simply gets to operate on the public API after successful construction, and is perfectly safe. In fact, it really would be better thought of as a different construct:
Now we have separated the constructs with the
using
keyword (or awith
keyword). Construction with blocks have no keyword.This means that this case is now clear:
Now, personally I've never found post-construction with blocks (1) to actually be a feature I especially needed. So, maybe they are just a feature to drop altogether?
Anyway, I'm suggesting that treating (1) as the same as (2) and (3) is misleading, and complicating the discussions on constructors.
tompalmer Wed 16 Jul 2008
I prefer
obj.{/*...*/}
style for non-construction "with" blocks, but that's already been discussed before. I'm not sure I want to fight on that subject again. However, I do agree with Brian that post-construction "with" blocks are very nice.We especially can't say
using(obj) {/*...*/}
since that exact syntax means something very different in C#.brian Wed 16 Jul 2008
Stephen, all very good points - I think your post helps clarify how with-blocks are used.
It is interesting to note the serialization case, because currently Fan has no mechanism to verify a deserialized object, and that is something I think we'll need. Java doesn't really have this problem since it has a binary serialization syntax, so it is unlikely to be hand coded. Fan serialized objects will often be hand coded.
I think they are quite useful, because they still allow you to initialize objects created from factories (assuming non-const fields of course). Plus it is extremely common to do set of things to a given object. Couple examples:
I don't know if you've done a lot of Fan programming, but it really does change how you write code.
Agreed. I am not in favor of using two different syntaxes for with-blocks. I understand where you are coming from Stephen, but it really is just one syntax with different rules. It isn't much different than saying you can set const fields in a constructor but not another method - that is a rule that doesn't require two different assignment operators.
jodastephen Wed 16 Jul 2008
Firstly, I'd note that all JDK Java classes are protected against malicious hacking of the binary serialized format. Fan will need the same protection. And that requires validation of the created object after deserialization (and also applying any possible singleton caches).
The problem with one syntax for type (1) and (2) is as follows:
brian Wed 16 Jul 2008
My basic position is that you need two methods, one before and one after. You can't really do without either one.