RSS 2.0

Personal Info:

Joe Send mail to the author(s) is the lead developer and architect for Parallel Extensions to .NET, tinkers with type systems, and is an author and speaker.

Blogroll:
Other
News
 C|Net
 Kuro5hin
 The Register
Technology
 <?xmlhack?>
 Daily WTF
 DevX
 Hacknot
 Java Today
 Microsoft Top 10 Downloads
 MSDN
 MSDN: "Longhorn"
 MSDN: XML Developer Center
 Slashdot
 Techdirt
 theserverside.com
 W3C
 Web Pages That Suck
 XML Cover Pages
 XML Journal
 xml.com
Technology Blogs
 Aaron Skonnard [PluralSight]
 Adam Bosworth [Google]
 Andy Rich [MS/C++]
 Arpan Desai [MS/XML]
 BCL Team [MS]
 Bill Clementson [Lisp]
 Bill de hÓra
 Bruce Eckel [J]
 Bruce Tate [J]
 Casey Chestnut
 Cedric Beust [Google]
 Chris Anderson [MS/Avalon]
 Chris Lyon [MS]
 Christian Weyer
 Clemens Vasters [newtelligence]
 Craig Andera [PluralSight]
 Dan Sugalski [Parrot]
 Daniel Cazzulino
 Dave Chappel
 Dave Roberts [Lisp]
 Dave Thomas [PragProg]
 Dave Winer
 Dion Almaer [J]
 Don Demsak
 Doug Purdy [MS/Indigo]
 Drew Marsh
 Eric Gunnerson [MS]
 Eric Rudder [MS]
 Eric Sink
 Fritz Onion [PluaralSight]
 Gavin King [J/Hibernate]
 Grady Booch [IBM]
 Hervey Wilson [MS/Indigo]
 Hillel Cooperman [MS/Shell]
 Howard Lewis Ship [J/Apache]
 Ingo Rammer [PluralSight]
 James Gosling [J/Sun]
 James Strachan [J/Groovy]
 Jason Matusow [MS/OSS]
 Jeffrey Schlimmer [MS/Indigo]
 Joe Beda [Google]
 Joel Spoelsky
 Jon Udell
 Josh Ledgard [MS/Evang]
 Joshua Allen [MS]
 Lambda
 Larry Osterman [MS]
 Maoni Stephens [MS/CLR]
 Mark Fussell [MS/XML]
 Martin Fowler
 Martin Gudgin [MS/Indigo]
 Me
 Michael Howard [MS]
 Miguel de Icaza [Mono]
 Mike Clark
 Omri Gazitt [MS/Indigo]
 Pat Helland [MS/PAG]
 Pinku Surana
 Raymond Chen [MS]
 Rich Lander [MS/CLR]
 Rob Howard
 Rob Relyea [MS/Avalon]
 Robert Cringely
 S. Somasegar [MS/DevDiv]
 Sam Gentile
 Scoble [MS/Evang]
 Scott Guthrie [MS/WebNet]
 Scott Hanselman
 Sean McGrath [J]
 Simon Fell
 Stanley Lippman [MS/C++]
 Steve Maine
 Steve Swartz [MS/Indigo]
 Steve Vinoski
 Steven Clarke [MS/Usability]
 Stuart Halloway
 Ted Leung
 Ted Neward [DM]
 Tim Bray [Sun]
 Tim Ewald [Mindreef]
 Tim O'Reilly
 Werner Vogels [Amazon]
 Wintellect
 Yasser Shohoud [MS/Indigo]
Top 20
 Brad Abrams [MS/CLR]
 Chris Brumme [MS/CLR]
 Chris Sells [MS/Ultra]
 Cyrus Najmabadi [MS/C#]
 Dominic Cooney [MS/XAF]
 Don Box [MS/Ultra]
 Don Syme [MS/R]
 Guido van Rossum [Python]
 Herb Sutter [MS/C++]
 Ian Griffiths
 Jason Zander [MS/CLR]
 Jim Hugunin [MS/CLR]
 Joel Pobar [MS/CLR]
 Krzysztof Cwalina [MS/CLR]
 Patrick Logan
 Paul Graham
 Rico Mariani [MS/CLR]
 Rory Blyth [MS/DN]
 Sam Ruby
 Wesner Moise
VC/Business Blogs
 Ed Sim
 Fred Wilson
 Jonathan Schwartz [J/Sun]
 Lawrence Lessig [Stanford]
 Mark Cuban
 Michael Hyatt
 Pierre Omidyar
 Ross Mayfield
 VentureBlog
 Weekly Read
Wine, Food & Tea
 The Silk Road of Wine
 Vinography: a wine blog
 Wine Whys

Disclaimer:
The content of this site are my own personal opinions and do not represent my employer's view in anyway.

© 2009, Joe Duffy

 
 Sunday, November 11, 2007

I’ve been asked a number of times about immutable types support for C#.  Although C# doesn't offer first class language support in the way that F# does, you can get pretty far with what you do have in your hands already.  Nothing prevents you from creating immutable data structures today, of course, but the problem is that there’s no compiler or runtime support to ensure you’ve done it right.

I just hacked together some new attributes and a handful of FxCop rules as an experiment.  I’ve been very happy with the result.  Sure it’s not baked into the language, but it’s a start.  If there’s any interest, I can make the code available so you can play with it too.

Attributes and analysis

Imagine we had an ImmutableAttribute.  Annotating a type with it indicates that objects of that particular type are immutable, i.e. that their state never changes after being constructed.  This is great from a concurrency standpoint because it means access to such objects do not require synchronization.  This can lead to more efficient code that not only has a higher chance of being correct but is also vastly easier to maintain.  Well, what kind of restrictions would such a type be subject to?

1. Immutable types must have only initonly fields.

The first rule takes advantage of existing CLR type system and language support for initonly fields (a.k.a. readonly in C#).  Marking a field as initonly ensures it is never written to after the constructor has finished executing.  So long as all fields are initonly, the class is effectively already “shallow” immutable.

2. Immutable types must only contain fields of immutable types.

The second rule ensures transitivity, or “deep” immutability.  The mutability of a complex object is typically, but not always, comprised of not only its own fields but also the state in the objects it refers to.  With this rule and the prior rule, we’re about 90% there.

3. Immutable types should only subclass other immutable types.

To give the appearance that a particular object is immutable, that object’s type must not depend on other types that are mutable, as articulated by the previous rule.  The ‘base’ reference is effectively just another field, and so this rule is derived from the previous one.  If an immutable type could inherit mutable members and fields, then it wouldn’t really be immutable after all.

4. Mutable types should not subclass immutable types.

Similar to the previous rule, it is also a bad thing if a mutable subtype can override behavior from the subtype, giving the appearance of mutability.  Say we have an immutable class IC with a virtual method f, and some mutable subclass MC overrides f to introduce logic that logically mutates the state of an object.  Although the rules above are sufficient to ensure that the object is physically immutable, this can circumvent immutability safety through polymorphism.  A related piece of advice: public immutable types should be sealed, to prevent outside classes that do not abide by immutability analysis from breaking code which assumes a given type is immutable.  Alternatively, virtual members can be eliminated.

5. Immutable types must not leak the ‘this’ reference during construction.

This rule is subtle.  Although initonly ensures fields are never written to after construction, these fields may be written to any number of times while an object is actively being constructed.  If some code called during construction publishes a reference to the object (e.g. by storing it in a static variable), then other threads might access the object while it still appears to be mutable.  They may witness a partially initialized object, an object whose fields are still changing, and so on.

And that’s it!  5 simple rules.  It may sound complicated, but the code to perform the static analysis for all but the last one is straightforward and a dozen-or-so lines apiece.  A few things are worth mentioning.  First, the CLR’s memory model ensures that, after an object is constructed and published, reads of its fields cannot be reordered to break immutability.  Additionally, there are many immutable types in the CLR today that are not verified as such.  So in my analysis rules, I have hard-coded a set of well known immutable types so that you can use them w/out problem: Object, String, DateTime, TimeSpan, Boolean, Byte, SByte, Int16, Int32, Int64, IntPtr, UInt16, UInt32, UInt64, UIntPtr, Decimal, Double, Single, and ValueType.  Ideally if we supported this in the .NET Framework, we'd annotate them.

Impact to imperative programming

Programming in an immutable world is rather tricky.  As someone who has done most of his programming for the past 10+ years in C-style languages, I just take for granted that data structures change over time.  With immutability, there tends to be a whole lot more copying and functional-style function calling, where data structures are passed as an input argument and the “mutated” copy is returned as an output argument.  I’m trying to kick the mutability habit, since I fully believe immutability is one key to being successful with massive degrees of parallelism.  And it usually leads to cleaner code too.

But it’s hard.  Using something as simple as an array field on an immutable type will fail the above rules, since the CLR’s array types are mutable.  I’ll explore building one below, but this probably points to a need for better immutability support in the .NET Framework.  It’s not too difficult to imagine providing base classes for common needs when building immutable data structures.

Circumventing the analysis

As you begin to explore immutable types in a bit more depth, you’ll realize there are often cases where immutability-by-cleverness is possible.  That is to say, although one or more of the rules above have been violated, the end result still appears to be immutable.  I can build an immutable list out of a linked list to avoid depending on CLR arrays, and mark the nodes as immutable, but they must refer to elements stored within the list.  Should we require the elements to also be immutable?  Perhaps, but perhaps not, depending on whether you consider the state of the list to also include the state of the elements inside it.  Usually that wouldn't be the case.  And, besides, if we know what we’re doing, we can create an immutable list based on an array anyway, which enables O(1) IList<T>-style random access.  We just need to be careful to encapsulate the array object and to never store an element into it post-construction.

To facilitate working around some of the rules in ways that are often necessary, I have provided options on ImmutableAttribute to suppress certain checks.  Additionally, there is a MutableAttribute which can mark certain fields to indicate they are not subject to the same restrictions as other fields on an immutable type.

An ImmutableList<T>

As an illustration, here is an ImmutableList<T>.  It implements IList<T>, but sadly it must throw exceptions in several circumstances because both IList<T> and ICollection<T> offer methods that are intrinsically mutable.  Undoubtedtly there are bugs because I whipped it up quickly and have omitted a lot of needed error checking.  I just wanted to give the general idea of how it might be done:

/// <summary>
/// A list that has been written to be observationally immutable.  A mutable array
/// is used as the backing store for the list, but no mutable operations are offered.
/// </summary>
/// <typeparam name="T">The type of elements contained in the list.</typeparam>
[Immutable]
public sealed class ImmutableList<T> : IList<T>
{

    [Mutable]
    private readonly T[] m_array;

    /// <summary>
    /// Create a new list.
    /// </summary>
    public ImmutableList()
    {
        m_array = new T[0];
    }

    /// <summary>
    /// Create a new list, copying elements from the specified array.
    /// </summary>
    /// <param name="arrayToCopy">An array whose contents will be copied.</param>
    public ImmutableList(T[] arrayToCopy)
    {
        m_array = new T[arrayToCopy.Length];
        Array.Copy(arrayToCopy, m_array, arrayToCopy.Length);
    }

    /// <summary>
    /// Create a new list, copying elements from the specified enumerable.
    /// </summary>
    /// <param name="enumerableToCopy">An enumerable whose contents will
    /// be copied.</param>
    public ImmutableList(IEnumerable<T> enumerableToCopy)
    {
        m_array = new List<T>(enumerableToCopy).ToArray();
    }

    /// <summary>
    /// Retrieves the immutable count of the list.
    /// </summary>
    public int Count
    {
        get { return m_array.Length; }
    }

    /// <summary>
    /// A helper method used below when a mutable method is accessed.  Several
    /// operations on the collections interfaces IList&lt;T&gt; and
    /// ICollection&lt;T&gt; are mutable, so we cannot support them.  We offer
    /// immutable versions of each.
    /// </summary>
    private static void ThrowMutableException(string copyMethod)
    {
        throw new InvalidOperationException(
            String.Format("Cannot mutate an immutable list; " +
            "see copying method '{0}'", copyMethod));
    }

    /// <summary>
    /// Whether the list is read only: because the list is immutable, this
    /// is always true.
    /// </summary>
    public bool IsReadOnly
    {
        get { return true; }
    }

    /// <summary>
    /// Accesses the element at the specified index.  Because the list is
    /// immutable, the setter will always throw an exception.
    /// </summary>
    /// <param name="index">The index to access.</param>
    /// <returns>The element at the specified index.</returns>
    public T this[int index]
    {
        get
        {
            return m_array[index];
        }
        set
        {
            ThrowMutableException("CopyAndSet");
        }
    }

    /// <summary>
    /// Copies the list and adds a new value at the end.
    /// </summary>
    /// <param name="value">The value to add.</param>
    /// <returns>A modified copy of this list.</returns>
    public ImmutableList<T> CopyAndAdd(T value)
    {
        T[] newArray = new T[m_array.Length + 1];
        m_array.CopyTo(newArray, 0);
        newArray[m_array.Length] = value;
        return new ImmutableList<T>(newArray);
    }

    /// <summary>
    /// Returns a new, cleared (empty) immutable list.
    /// </summary>
    /// <returns>A modified copy of this list.</returns>
    public ImmutableList<T> CopyAndClear()
    {
        return new ImmutableList<T>(new T[0]);
    }

    /// <summary>
    /// Copies the list and modifies the specific value at the index provided.
    /// </summary>
    /// <param name="index">The index whose value is to be changed.</param>
    /// <param name="item">The value to store at the specified index.</param>
    /// <returns>A modified copy of this list.</returns>
    public ImmutableList<T> CopyAndSet(int index, T item)
    {
        T[] newArray = new T[m_array.Length];
        m_array.CopyTo(newArray, 0);
        newArray[index] = item;
        return new ImmutableList<T>(newArray);
    }

    /// <summary>
    /// Copies the list and removes a particular element.
    /// </summary>
    /// <param name="item">The element to remove.</param>
    /// <returns>A modified copy of this list.</returns>
    public ImmutableList<T> CopyAndRemove(T item)
    {
        int index = IndexOf(item);
        if (index == -1)
        {
            throw new ArgumentException("Item not found in list.");
        }

        return CopyAndRemoveAt(index);
    }

    /// <summary>
    /// Copies the list and removes a particular element.
    /// </summary>
    /// <param name="index">The index of the element to remove.</param>
    /// <returns>A modified copy of this list.</returns>
    public ImmutableList<T> CopyAndRemoveAt(int index)
    {
        T[] newArray = new T[m_array.Length - 1];
        Array.Copy(m_array, newArray, index);
        Array.Copy(m_array, index + 1, newArray, index, m_array.Length - index - 1);
        return new ImmutableList<T>(newArray);
    }

    /// <summary>
    /// Copies the list adn inserts a particular element.
    /// </summary>
    /// <param name="index">The index at which to insert an element.</param>
    /// <param name="item">The element to insert.</param>
    /// <returns>A modified copy of this list.</returns>
    public ImmutableList<T> CopyAndInsert(int index, T item)
    {
        T[] newArray = new T[m_array.Length + 1];
        Array.Copy(m_array, newArray, index);
        newArray[index] = item;
        Array.Copy(m_array, index, newArray, index + 1, m_array.Length - index);
        return new ImmutableList<T>(newArray);
    }

    /// <summary>
    /// This method is unsupported on this type, because it is immutable.
    /// </summary>
    void ICollection<T>.Add(T item)
    {
        ThrowMutableException("CopyAndAdd");
    }

    /// <summary>
    /// This method is unsupported on this type, because it is immutable.
    /// </summary>
    void ICollection<T>.Clear()
    {
        ThrowMutableException("CopyAndClear");
    }

    /// <summary>
    /// Checks whether the specified item is contained in the list.
    /// </summary>
    /// <param name="item">The item to search for.</param>
    /// <returns>True if the item is found, false otherwise.</returns>
    public bool Contains(T item)
    {
        return Array.IndexOf<T>(m_array, item) != -1;
    }

    /// <summary>
    /// Copies the contents of this list to a destination array.
    /// </summary>
    /// <param name="array">The array to copy elements to.</param>
    /// <param name="index">The index at which copying begins.</param>
    public void CopyTo(T[] array, int index)
    {
        m_array.CopyTo(array, index);
    }

    /// <summary>
    /// Retrieves an enumerator for the list's collections.
    /// </summary>
    /// <returns>An enumerator.</returns>
    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < m_array.Length; i++)
        {
            yield return m_array[i];
        }
    }

    /// <summary>
    /// Retrieves an enumerator for the list's collections.
    /// </summary>
    /// <returns>An enumerator.</returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<T>)this).GetEnumerator();
    }

    /// <summary>
    /// Finds the index of the specified element.
    /// </summary>
    /// <param name="item">An item to search for.</param>
    /// <returns>The index of the item, or -1 if it was not found.</returns>
    public int IndexOf(T item)
    {
        return Array.IndexOf<T>(m_array, item);
    }

    /// <summary>
    /// This method is unsupported on this type, because it is immutable.
    /// </summary>
    void IList<T>.Insert(int index, T item)
    {
        ThrowMutableException("CopyAndInsert");
    }

    /// <summary>
    /// This method is unsupported on this type, because it is immutable.
    /// </summary>
    bool ICollection<T>.Remove(T item)
    {
        ThrowMutableException("CopyAndRemove");
        return false;
    }

    /// <summary>
    /// This method is unsupported on this type, because it is immutable.
    /// </summary>
    void IList<T>.RemoveAt(int index)
    {
        ThrowMutableException("CopyAndRemoveAt");
    }

}

I won’t spend much time going over this code.  Just notice that the type is marked with the ImmutableAttribute, the array field is marked with the MutableAttribute (since it’s not itself an immutable type and would fail the analysis otherwise), and that any operations that modify the list must make a copy of the entire thing.

Summary

This has been an interesting exercise.  Through it, I have come to realize that first class immutability in the type system is not such a farfetched dream.  The most onerous aspect to it is probably the restrictions it imposes on subclassing in the programming model, effectively bifurcating the type system into those types that are mutable and those types that are immutable.  But, in the end, I’m not so sure it’s too bad a problem: interchanging the two seems like a very bad idea anyway.

Feedback on all of this would be appreciated.  Do you see it as useful?  If you had it, would you use it in your programs today?  Do you believe that it is one step needed (of many!) to bring us towards a world in which building concurrent programs is simpler?

11/11/2007 2:11:41 PM (Pacific Standard Time, UTC-08:00)  #    Comments [23]

11/11/2007 3:47:13 PM (Pacific Standard Time, UTC-08:00)
"I just hacked together some new attributes and a handful of FxCop rules as an experiment. I’ve been very happy with the result. Sure it’s not baked into the language, but it’s a start. If there’s any interest, I can make the code available so you can play with it too."

Yeah, I'd love to see it.

"Do you see it as useful?" f you had it, would you use it in your programs today?

Yes, absolutely. If things don't change, then concurrent programming becomes easier. Having library support with compiler enforcements would be really great.

11/11/2007 7:02:48 PM (Pacific Standard Time, UTC-08:00)
Very interesting.

I've been thinking along the same lines for the last couple of months. I typically code objects to be immutable, but I'd much rather have a compiler and runtime that supports this natively. Here is the design I'd like to see in .NET.

public immutable class Address{
public string Street{ get; }
public string Zip{ get; }
}

The compiler should make it easy to create and modify the object like so:

Address zipOnly = new Address(Zip=>"60607");
Address complete = new Address(zipOnly, Street=>"Main St.");

With compiler enforced immutable objects, concurrency gets dead simple with a library such as Retlang, while still leveraging the full OO capabilities of .NET.

http://code.google.com/p/retlang/

Mike Rettig
Retlang Developer
11/11/2007 8:23:32 PM (Pacific Standard Time, UTC-08:00)
This is definitely a step in the right direction, no doubt. Whether it's though attributes/annotations:

[Immutable]
public sealed class MyData {}

or a keyword:

public sealed immutable class MyData {}

CodeAnalysis rules are great, but deeper (baked-in) support would be even better. I've always designed my classes to be immutable as a general rule-of-thumb and having the compiler and/or runtime supporting this would be very nice.
11/12/2007 1:40:18 AM (Pacific Standard Time, UTC-08:00)
Joe,

I would be very interested in playing with your code. Thanks in advanced for taking a bit of time to make it available.

Anthony
11/12/2007 3:38:22 AM (Pacific Standard Time, UTC-08:00)
To mark entire types as immutable may not be enough. I could imagine an immutable class that uses a hashtable to look up (immutable) values. But there is no implementation of an immutable hashtable or an other data structure...

Actually we need to mark operations (methods and properties) as immutable. With this approach it would be possible to use mutable types (such as List, Dictionary, etc.), but access the immutable methods (and properties) only (such as GetEnumerator(), Count, etc.)

A code analyser (or a compiler) could also verify that immutable methods (and properties) do not access mutable ones.
11/12/2007 6:54:19 AM (Pacific Standard Time, UTC-08:00)
This is definitively very useful. An attribute is fine as a first step (as long as FxCop is there to enforce it) but a keyword would make a great addition to C# 4.0. Hopefully this would even render the misleading 'readonly' keyword obsolete.
Why stop at data structures? Some of the Spec# stuff is very interesting. C# could become an even better language by supporting the immutable attribute/keyword together with the ability to state that a method is side-effect free (pure)... this would open-up a whole lot of new possibilities.

PS: can you post the FxCop rules? Thanks.
Franck Jeannin
11/12/2007 9:45:04 AM (Pacific Standard Time, UTC-08:00)
As a corollary to support like this, I would also like to see something like a "readonly" keyword available within functions to indicate that the variable in question will not be modified after its assignment. I know it's not entirely on-topic, but I've been waiting for someone at Microsoft to blog about immutability for a while. ;)
11/12/2007 11:42:01 AM (Pacific Standard Time, UTC-08:00)
I tried to post this last night, but apparently failed. Angle brackets in this comment changed to square brackets to avoid ASP.NET barfing.

It's worth pointing out that the ability to create immutable types is *not* the same as const correctness, which - while desirable in many ways - is significantly more complicated.

How much support would there have to be, ultimately? I suppose that in order to verify the rules, the compiler would need to follow mutability, i.e. know about the extra attribute. We'd also need to apply that attribute to a bunch of existing typess, as mentioned in the post. Oh, and make the C# compiler emit the attribute when generating anonymous classes, preferrably. If we *do* care about things like the list's elements themselves mutating, would an extra generic constraint be useful, indicating that the type parameter had to be immutable?

One problem I see is that a level of trust is required - you've got to mark the array as mutable yourself, and be careful not to use it in a "bad way". With more thought, would it be possible to analyse all uses of a field decorated with [Mutable]? In particular, is it possible for the CLR to verify immutability to avoid me marking something as immutable and then messing around with it?

For the specific questions:
1) Yes, I see it as useful.
2) Yes, I would use it in my programs today, and encourage others to.
3) I believe it's a *positive* step in making concurrent programming simpler. It may not be "needed", but that doesn't mean it's not beneficial.

Making the F# immutable collections available in the framework will help a lot too, of course. That brings me onto the next point - IList[T]. A lot of the code in your sample just throws exceptions, but you've got to include the methods in order to implement IList[T]. Implementing ICollection[T] would mean fewer of those methods, but would make it less useful. Implementing IEnumerable[T] would remove all of the "throwing" methods but again limit the usefulness.

Could I suggest that it would be useful to have an interface in the framework which *only* exposes read-only operations, but which *does* expose two useful members which IEnumerable[T] lacks: an indexer and a count. Including a "read-only" flag as per ICollection[T] could be useful too.

Now, I don't know the details of interface compatibility well enough to know whether it's possible to inject another interface into the hierarchy without breaking compatibility left, right and centre - this could be something between IEnumerable[T] and ICollection[T], effectively. I can't immediately think how it would break anything, but the little I *do* know about compatibility is that there's a huge gulf between not being able to think of a counterexample immediately and there not *being* a counterexample.

As for bugs in the code - my only contribution is to spot that there's an "adn" instead of "and" in one of the comments :)
11/13/2007 12:43:05 AM (Pacific Standard Time, UTC-08:00)
One thing to watch out for is that the strings returned from StringBuilder.ToString() are not immutable. If you add more data to the StringBuilder object, this will change the context of the string you got from StringBuilder.ToString().
Thomas Olsson
11/13/2007 1:13:59 AM (Pacific Standard Time, UTC-08:00)
The strings returned from StringBuilder.ToString() are immutable. The correctness can be verified with the following code:

StringBuilder b = new StringBuilder();
b.Append("text");
string s = b.ToString();
Console.WriteLine(s); // output: text
b.Append(" suffix");
Console.WriteLine(s); // output: text
11/13/2007 1:19:56 AM (Pacific Standard Time, UTC-08:00)
An other positive "side effect" of immutable types is the clone-safety. Immutable types do not have to be deep-cloned (by using the ICloneable interface or copy constructors), a copy of the reference to an immutable type is sufficient.
11/13/2007 8:45:37 AM (Pacific Standard Time, UTC-08:00)
Very interesting work. Yes, certainly useful. One quibble: AFAICS, there's nothing C#-specific about this. It should work fine for any .NET language.
11/13/2007 11:43:09 AM (Pacific Standard Time, UTC-08:00)
Thomas's comment, while not strictly correct, raises an interesting point. We all talk about strings being immutable, but they aren't *within mscorlib*. StringBuilder does indeed change the contents of a string, it's just that it makes sure that when that string is made publicly available, it doesn't change it any more after that.

I'm not sure whether it's worth that having a separate type of declared immutability, however - it would be incredibly painful to verify.

Jon
11/13/2007 6:05:25 PM (Pacific Standard Time, UTC-08:00)
+1 vote for concurrency attributes being useful.

There is feedback on the Visual Studio/.NET Framework section of Microsoft Connect suggesting adding some Java-style "FindBugs" attributes including concurrency related ones, e.g.:

net.jcip.annotations.GuardedBy
net.jcip.annotations.Immutable
net.jcip.annotations.NotThreadSafe
net.jcip.annotations.ThreadSafe

Would this be a useful starting point?

ref: https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=278371

Cheers,
-Mat
11/14/2007 3:04:06 AM (Pacific Standard Time, UTC-08:00)
Jon Skeet,

Note that the framework already has the generic collection:
System.Collections.ObjectModel.ReadOnlyCollection[T]
This has the indexer and the count that you desire.

I only mention this because many people only include the System.Collections.Generic namespace and not the ObjectModel namespace.

One of the main features Joe had on his ImmutableList[T] was methods for adding, removing (etc) elements in a immutable way (by returning a new list).

Regards,

Dav
11/14/2007 12:45:24 PM (Pacific Standard Time, UTC-08:00)
Hmm, the ReadOnlyCollection is just a read-only wrapper around the non-read-only collection. If the non-read-only collection changes, the ReadOnlyCollection becomes itself updated.

Regards,

Michael Damatov
11/14/2007 1:23:22 PM (Pacific Standard Time, UTC-08:00)
Dav: ReadOnlyCollection[T] is fine as a *class*, but it's not much help if I want to expose it as an *interface* that I implement.

Jon
11/14/2007 4:03:44 PM (Pacific Standard Time, UTC-08:00)
Hi Joe,

Some folks over at CodeProject were talking about C#, concurrency, and shared mutable state. I think you might find it interesting:

http://www.codeproject.com/lounge.asp?msg=2318898#xx2318898xx
11/15/2007 12:32:07 AM (Pacific Standard Time, UTC-08:00)
Just in case you haven't seen it yet, a very interesting discussion on the same topic from Eric Lippert's blog:
http://blogs.msdn.com/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx
Franck Jeannin
11/20/2007 5:09:41 PM (Pacific Standard Time, UTC-08:00)
I have implemented an 'immutable' collection class in DomainObjects (http://domainobjects.svn.sourceforge.net/viewvc/domainobjects/trunk/DomainObjects2/src/Core/DomainObjects/Facade/Domain/Collection.cs?view=markup) to manage one-to-many relationships in an ACID manner. In the real world we need to modify the relationships between objects, so the questions are: 'In my code, what should be the scope of mutability? Who should see my collection modifications before they are viewable by the rest of the world?'

Well, DomainObjects keeps the scope of mutation at the thread/transaction level: before modifying the collection, a thread-local copy of the original collection items is made and only that copy gets modified. Methods on the call-stack can see the modification because they are in the context of the thread/transaction, but other threads cannot. Only when the transaction is committed can other threads see the modifications.

No locks are necessary and the modifications are isolated and thread safe.

This scheme is also used to modify shared objects without using locks: before an object is modified, a thread-local shallow copy of the object is made, and only the local copy is modified. Again, no locks are necessary and we are able to implement a highly concurrent system. (Our optimistic concurrency infrastructure detects when 'shared' state has been concurrently modified.)

I believe that when we talk about the need for 'immutability' we are talking about the need to correctly scope the visibility to our modifications: Within the function scope (ala functional programming)? Within the transaction scope (ala database programming)? Within the middle-tier transaction/thread scope (ala object-relational mapping)?
2/7/2008 2:09:42 PM (Pacific Standard Time, UTC-08:00)
I haven't read this post for a while, but I see a contradiction between rules 3 and 4. As a reminder:

[quote]
3. Immutable types should only subclass other immutable types.
4. Mutable types should not subclass immutable types.
[/quote]

So, should System.Object be mutable or not? If it's deemed mutable, you can't create any immutable types - at some point there would have to be an immutable type subclassing System.Object, which breaks rule 2.

If it's deemed immutable, that breaks every mutable type - because sooner or later there would have to be a mutable type subclassing System.Object, breaking rule 3. Bye bye, StringBuilder.


Basically, I think System.Object has to be treated as a special case :)

Jon
2/7/2008 2:10:49 PM (Pacific Standard Time, UTC-08:00)
(Damn it, I got the numbers wrong in the above comment. In the explanation, I meant rule 3 when I wrote rule 2, and meant rule 4 when I wrote rule 3. Sorry folks.)
2/20/2008 1:52:28 PM (Pacific Standard Time, UTC-08:00)
As
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

 

Recent Entries:

Search:

Browse by Date:
<January 2009>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567

Browse by Category:

Notables: