[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