[nem-en] Initonly modifier for immutable fields
Kamil Skalski
kamil.skalski at gmail.com
Sun Jul 9 12:21:05 CEST 2006
Recently I implemented a change in compiler to emit .NET's 'initonly'
modifier on class fields, which are immutable (see bug
http://nemerle.org/bugs/view.php?id=703).
Unfortunately it caused some more ugly problems, which I was trying to
solve till now.
The current solution triggered 3 user visible changes in the language:
- the closurized access to initonly field from constructor:
from now on following code is invalid:
class A {
foo : int;
public this () {
def bar (x) {
foo = x;
}
if (waz)
bar (1);
else
bar (2);
}
}
because 'bar' local function is translated to a method, which cannot
assign to initonly field.
This particular code could work if .NET supported some kind of
'initonly' attribute on methods, but the is even more problematic
example:
class A {
foo : int;
func : int -> void;
public this () {
def bar (x) {
foo = x;
}
if (waz)
bar (1);
else
bar (2);
func = bar;
}
}
Now the function is stored in field and theoretically it could be
executed anytime. Compiler will just reject both code patterns.
- assignment to initonly field from base class in subclass' constructor:
class A {
protected foo : int;
}
class B : A {
public this () {
base ();
foo = 3;
}
}
is now rejected by compiler, since such code does not verify in .NET -
we now require more strict rule, that you can assign to immutable
field only in constructor of class it is defined in.
- initonly field containing internally mutable value types:
The most ugly change of behaviour I observed when running and
debugging our test suite is
how .NET forces such code to be compiled to:
[Record]
struct S {
public mutable muta : string;
public Modify (val : string) : void
{
muta = val;
}
}
[Record]
class A {
public mystruct : S;
}
def x = A (S ("first"));
System.Console.WriteLine (x.mystruct.muta);
x.mystruct.Modify ("second");
System.Console.WriteLine (x.mystruct.muta);
will print:
first
first
Since 'mystruct' is now initonly, we can only read its *copy*, we
cannot access a pointer (address) to it and effectively every
operation on it is always performed on a copy not on the original
struct.
If you try to assign directly to muta by
x.mystruct.muta = "second";
compiler will detect its inability to modify structure in place and
signal error.
This behaviour is now consistent with what C# compiler does for
'readonly' modifier.
The conclusion is that we have quite unpleasant changes in semantics
(the third one) and accepted programs. Of course we are open for
debate - maybe we should revert the changes and do not mark immutable
fields with .NET's initonly modifier.
The modifier gives as a few advantages though:
- it is a native (proper) mark for immutable fields in .NET
- it may allow runtime to do some more aggressive optimizations
- it can be utilized by Boogie theorem prover, which is being
integrated into Nemerle contracts functionality (similar to what Spec#
has)
--
Kamil Skalski
http://nazgul.omega.pl
More information about the devel-en
mailing list