What Is the Future of C# Anyways?

It was often asked during some of my presentations on F# and Functional C# about the future direction of C# and where I think it's going.  Last night I was pinged about this with my F# talk at the Philly ALT.NET meeting.  The question was asked, why bother learning F#, when eventually I'll get these things for free once they steal it and bring it to C#.  Being the language geek that I am, I'm pretty interested in this question as well.  Right now, the language itself keeps evolving at a rather quick pace as compared to C++ and Java.  And we have many developers that are struggling to keep up with the evolution of the language to a more functional style with LINQ, lambda expressions, lazy evaluation, etc.  There are plenty of places to go with the language and a few questions to ask along the way.

An Interview With Anders Hejlsberg

Recently on Software Engineering Radio, Anders Hejlsberg was interviewed about the past, present and future of C# on Episode 97.  Of course there are interesting aspects of the history of his involvement with languages such as Tubro Pascal and Delphi and some great commentary on Visual Basic and dynamic languages as well.  But, the real core of the discussion was focused around what problems are the ones we need to solve next?  And how will C# handle some of these features?  Will they be language constructs or built-in to the framework itself? 

Let's go through some of the issues discussed.

Concurrency Programming

Concurrency programming is hard.  Let's not mince words about it.  Once we start getting into multiple processors and multiple cores, this becomes even more of an issue.  Are we using the machine effectively?  It's important because with the standard locks/mutexes it's literally impossible to have shared memory parallelism with more than two processors without at some point being blocking and serial. 

The way things are currently designed in the frameworks and the languages themselves are not designed for concurrency to make it easy.  The Erlang guys of course would disagree since they started with that idea from the very start.  Since things are sandboxed to a particular thread, they are free to mutate state to their heart's content, and then when they need to talk to another process, they pick up the data and completely copy it over, so there is a penalty for doing so.  Joe Armstrong, the creator of Erlang, covered a lot of these things in his Erlang book "Programming Erlang: Software for a Concurrent World ".

Mutable State

Part of the issue concerning concurrency is the idea of mutable state.  As far back as I remember, we were always taught in CS classes that you can feel free to mutate state as need be.  But, that only really works when you've got a nicely serial application where A calls B calls C calls D and all on the same thread.  But, that's a fairly limiting thing idea as we start to scale out to multiple threads, machines and so on.  Instead, we need to focus on the mutability and control it in a meaningful way through not only the use of our language constructs, but our design patterns as well.

In the C# world, we have the ability to create opt-in immutability through the use of the readonly keyword.   This is really helpful to decide those fields that we don't really need to or want to modify.  This also helps the JIT better determine the use of our particular variable.  I'm not sure about performance gains, but that's not really the point of it all, anyways.  Take the canonical example of the 2D point such as this:

public class Point2D
{
    private readonly double x;
    private readonly double y;

    public Point2D() { }

    public Point2D(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public double X { get { return x; } }

    public double Y { get { return y; } }

    public Point2D Add(Size2D size)
    {
        return new Point2D(x + size.Height, y + size.Width);
    }
}

We've created this class as to not allow for mutable state, instead returning a new object that you are free to work with.  This of course is a positive thing.  But, can we go further in a language than just this?  I think so, and I think Anders does too.  Spec# and design by contract can take this just a bit further in this regard.  What if I can state that my object, as it is, is immutable?  That would certainly help the compiler to optimize.  Take for example doing Value Objects in the Domain Driven Design world.  How would something like that look?  Well, let's follow the Spec# example and mark my class as being immutable, meaning that once I initialize it, I cannot change it for any reason:

[Immutable]
public class Point2D
{
   // Class implementation the same
}

This helps make it more transparent to the caller and the callee that what you have cannot be changed.  This enforces the behaviors for my member variables in a pretty interesting way.  Let's take a look at the actual C# generated in Spec# for the above code.  I'll only paste the relevant information about what it did to the properties.  I'll only look at the X, but the identical happened for the Y as well.

public double X
{
    [Witness(false, 0, "", "0", ""), Witness(false, 0, "", "1", ""), Witness(false, 0, "", "this@ClassLibrary1.Point2D::x", "", Filename=@"D:\Work\SpecSharpSamples\SpecSharpSamples\Class1.ssc", StartLine=20, StartColumn=0x21, EndLine=20, EndColumn=0x22, SourceText="x"), Ensures("::==(double,double){${double,\"return value\"},this@ClassLibrary1.Point2D::x}", Filename=@"D:\Work\SpecSharpSamples\SpecSharpSamples\Class1.ssc", StartLine=20, StartColumn=20, EndLine=20, EndColumn=0x17, SourceText="get")]
    get
    {
        double return value = this.x;
        try
        {
            if (return value != this.x)
            {
                throw new EnsuresException("Postcondition 'get' violated from method 'ClassLibrary1.Point2D.get_X'");
            }
        }
        catch (ContractMarkerException)
        {
            throw;
        }
        double SS$Display Return Local = return value;
        return return value;
    }
}

What I like about F# and functional programming is the opt-out mutability, which means by default, my classes, lists, structures and so on are immutable by default.  So, this makes you think long and hard about any particular mutability you want to introduce into your program.  It's not to say that there can be no mutability in your application, but on the other hand, you need to think about it, and isolate it in a meaningful manner.  Haskell takes a more hardline stance on the issue, and mutability can only occur in monadic expressions.  If you're not aware of what those are, check out F# workflows which are perfectly analogous.  But by default, we get code that looks like this and is immutable:

type Point2D = class
  val x : double
  val y : double
 
  new() = { x = 0.0; y = 0.0 }
 
  new(x, y) =
    {
      x = x
      y = y
    }

  member this.X
    with get() = this.x
   
  member this.Y
    with get() = this.y
end

So, as you can see, I'm not having to express the immutability, only the mutability if I so choose.  Very important differentiator.

Method Purity

Method purity is another important topic as we talk about concurrent programming and such.  What I mean by this is that I'm not going to modify the incoming parameters or cause some side effects, and instead I will produce a new object instead.  This has lasting effects if I'm going to be doing things on other threads.  Eric Evans talked about this topic briefly in his Domain Driven Design book on Supple Design.  The idea is to have side effect free functions as much as you can, and carefully control where you mutate state through intention revealing interfaces and so on.

But, how do you communicate this?  Well, Command-Query Separation gets us part of the way there.  That's the idea of having the mutation and side effects in your command functions where you return nothing, and then queries which return data but do not modify state.  Spec# can enforce this behavior as well.  To be able to mark our particular functions as being pure is quite helpful in communicating whether I can expect a change in state.  Therefore I know whether I have to manage the mutation in some special way.  To communicate something like that in Spec#, all I have to do is something like this:

[Pure]
public Point2D Add(Size2D size)
    requires size != null;
{
    return new Point2D(x + size.Height, y + size.Width);
}

This becomes part of the method contract and some good documentation as well for your system.

Asynchronous Communication and Messaging

Another piece of interest is messaging and process isolation.  The Erlang guys figured out a while ago, that you can have mutation as well as mass concurrency, fail safety and so on with process isolation.   Two ideas come to mind from other .NET languages.  An important distinction must be made between concurrency memory models between shared-memory and message passing concurrency.  Messaging and asynchronous communication are key foundations for concurrent programming. 

In F#, there is support for the mailbox processing messaging.  This is already popular in Erlang, hence probably where the idea came from.  The idea is that a mailbox is a message queue that you can listen to for a message that is relevant to the agent you've defined.  This is implemented in the MailboxProcessor class in the Microsoft.FSharp.Control.Mailboxes namespace.  Doing a simple receive is pretty simple as shown here:

#light

#nowarn "57"

open Microsoft.FSharp.Control.CommonExtensions
open Microsoft.FSharp.Control.Mailboxes

let incrementor =
  new MailboxProcessor<int>(fun inbox ->
    let rec loopMessage(n) =
      async {
              do printfn "n = %d" n
              let! message = inbox.Receive()
              return! loopMessage(n + message)
            }
    loopMessage(0))

Robert Pickering has more information about the Erlang style message passing here

Now, let's come back just a second.  Erlang also introduces another concept that Sing# and the Singularity OS took up.  It's a concept called the Software Isolated Process (SIP).  The idea is to isolate your processes in a little sandbox.  Therefore if you load up a bad driver or something like that, the process can die and then spin up another process without having killed the entire system.  That's a really key part of Singularity and quite frankly one of the most intriguing.  Galen Hunt, the main researcher behind this talked about this on Software Engineering Radio Episode 88.  He also talks about it more here on Channel9 and it's well worth looking at.  You can also download the source on CodePlex and check it out.

Dynamic C#?

As you can probably note, Anders is pretty much a static typing fan and I'd have to say that I'm also firmly in that camp as well.  But, there are elements that are intriguing such as metaprogramming and creating DSLs which are pretty weak in C# as of now.  Sure, people are trying to bend C# in all sorts of interesting ways, but it's not a natural fit as the language stands now.  So, I think there can be some improvements here in some areas.

Metaprogramming

Metaprogramming is another area that was mentioned as a particularly interesting aspect.  As of right now, it's not an easy fit to do this with C#.  But once again, F# has many of these features built-in to do such things as quotations to do some metaprogramming because that's what it was created to do, a language built to create other languages.  Tomas Petricek is by far one of the authorities on the subject as he has leveraged it in interesting ways to create AJAX applications.  You can read about his introduction to metaprogramming here and his AJAX toolkit hereDon Syme has also written a paper about leveraging Meta-programming with F# which you can find here.  But I guess I have to ask the question, does C# need this or shouldn't we just use F# for what it's really good at and not shoehorn yet another piece onto the language?  Or the same could be said of Ruby and its power with metaprogramming as well, why not use the best language for the job?

Dynamic Dispatch

The idea of dynamic dispatch is an interesting idea as well.  This is the idea that you can invoke a method on an object that doesn't exist, and instead, the system figures out where to send it.  In Ruby, we have the method_missing concept which allows us to define that behavior when that method that is being invoked is not found.  Anders thought it was an intriguing idea and it was something to look at.  This might help in the creation of DSLs as well when you can define that behavior even though that method may not exist at all.

In the Language or the Framework?

Another good question though is do these features belong in the language itself or the in the framework?  The argument here is that if you somehow put a lot of constraints on the language syntax, then you might prematurely age the language and as a result, decline in usage.  Instead, the idea is to focus on the libraries to make these things available.  For example, the MailboxProcessor functionality being brought to all languages might not be a bad idea.  Those sorts of concepts around process isolation would be more of a framework concept than a language concept.  But, it's an interesting debate as to what belongs where.  Because at the end of the day, you do need some language differentiation when you use C#, F#, Ruby, Python, C++, etc or else what's the point of having all of them?  To that point I've been dismayed that VB.NET and C# have mirrored themselves pretty well and tried to make themselves equal and I wish they would just stop.  Let VB find a niche and let C# find its niche. 

Conclusion


Well, I hope this little discussion got you thinking as well about the future of C# and the future of the .NET framework as well.  What does C# need in order to better express the problems we are trying to solve?  And is it language specific or does it belong in the framework?  Shouldn't we just use the best language for the job instead of everything by default being in C#?  Good questions to answer, so now discuss...

kick it on DotNetKicks.com

3 Comments

  • Great post.

    It would be interesting to see a pro-con of agent-based design + frameworks vs. language-based concurrency.

    I'm currently on the agent-based design side.

    That's not to say I don't think that a bit of tooling could help make it easier for developers to get started.

  • @Udi

    I'll have to think about it a bit more. The language based concurrency right now is based upon locks and monitors and that just doesn't work to scale right now. Instead, if you lead more towards SIPs in the framework itself could help. I don't know how the language itself can help, but let me think about it some more.

    Matt

  • What a joy to find someone else who thikns this way.

Comments have been disabled for this content.