IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

FORTRAN-Compatible Dynamic Objects in C#

Monday 1 April, 2013, 12:46 PM

The 4th version of C# (which shipped with Visual Studio 2010) added the dynamic keyword. This is most useful for dealing with COM scripting APIs such as Microsoft Office’s Automation features, which were a nightmare to work with in older versions of C#. But this language feature also makes it possible to use certain idioms that used to be the preserve of dynamic languages. For example, it lets you write this sort of questionable code:

dynamic o = new ExpandoObject();

o.Count = "1";
o.Count += 4;

Console.WriteLine(o.Count);

As you may or may not be expecting, this prints out 14.

C# lets the target object determine the semantics, i.e., not everything does the same thing when used through dynamic. In the example above, I’ve used ExpandoObject, which behaves in a way that vaguely resembles how objects work in JavaScript. In particular, if you set a property that didn’t previously exist, the object will automatically ‘expand’ by growing a new property of the specified name. (It’s not quite the same as JavaScript, which will even let you read a property that has never been set; you’ll get an exception if you try that with ExpandoObject in C#.)

This enables you to make mistakes like this, without any tiresome interference from the compiler:

dynamic o = new ExpandoObject();

o.Count = 1;
o.Cuont = o.Count + 1;

Console.WriteLine(o.Count);

Despite the fun I’ve been having with Clojure lately, I remain more of a static typing guy at heart, so I admit to being a little perplexed about why dynamic language fans prefer things their way. But presumably this example is precisely the sort of thing they’re banging on about when they boast about how much more productive their preferred languages are—it lets you get bugs like this straight into production, unencumbered by a compiler that might point out the mistake.

(If, despite choosing a dynamic language, this is the sort of thing you want to avoid, I gather the usual solution is to try to catch this sort of problem with unit tests instead. It’s easier to write a test to find this sort of bug than trying to test anything deeper about what your code is supposed to do—indeed it’s so easy that outside of the dynamic world, the compiler can do it for you. I presume the increased volume of shallow tests adds to the sensation dynamic language advocates get of feeling highly productive, at least insofar as producing high volumes of code constitutes productivity. And yet they’re so rude about ‘ceremony’ in languages they don’t like!)

When reviewing the chapter on C#’s dynamic keyword in my book, Programming C# 5.0, my father remarked “Shades of FORTAN IV!!” He’s been working with computers since he left university in the early 1960s, so he can bring some useful context to supposedly new developments in technology. As he explains:

“In FORTRAN you didn’t have to declare your variables before using them. If you used a particular name, it automatically created the variable for you. (Typing was implicit too—if the first letter of the variable’s name was in the range I to N the variable was an integer, anything else was a float. Early FORTRAN was very numeric-oriented—things like characters were really just numbers being treated in a particular way.)”

The parenthetical comment interested me, because it would enable us to avoid what might be a bug in the first example above—if the author of the code had intended the count to be an integer, then the result, 14, will not be suitable. If only we had FORTRAN IV’s ability to state what you mean by simply adding an ugly prefix to a variable name, we could get the right result while still using dynamically created properties. So I wrote a simple class to implement this:

class FortranIVObject : DynamicObject
{
    private readonly Dictionary<string, object> _values =
        new Dictionary<string, object>();

    public override bool TrySetMember(
        SetMemberBinder binder, object value)
    {
        EnsureShouting(binder.Name);

        char first = binder.Name[0];
        if (first >= 'I' && first <= 'M')
        {
            value = Convert.ToInt32(value);
        }

        _values[binder.Name] = value;

        return true;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        EnsureShouting(binder.Name);

        if (!_values.TryGetValue(binder.Name, out result))
        {
            char first = binder.Name[0];
            result = first >= 'I' && first <= 'M' ? (object) 0 : 0.0;
            _values[binder.Name] = result;
        }

        return true;
    }

    private static void EnsureShouting(string name)
    {
        if (name.Any(char.IsLower))
        {
            throw new ArgumentException(
                "FORTRAN identifiers must be UPPERCASE.");
        }
    }
}

I’m deriving from DynamicObject, because that does most of the work required to implement custom dynamic behaviour. We just have to write an override for each feature we’d like to control. In this case, when a member is set, we look for the letter prefix, and if that’s one of the letters indicating that the variable should be an integer, we coerce the incoming value. Notice that the code for getting a value is happy to let you read a variable without ever having initialized it—it defaults to a value of zero. This gets even more into the spirit of dynamic languages than JavaScript—although JavaScript doesn’t report an error when reading undefined variables, it does return a special undefined value, increasing the risk that defects in your code will be detected before you ship. Conversely, my implementation supplies a value that is more likely to allow bugs to go into production unnoticed. But that’s just icing on the cake. The main point here was to get our first example to work as intended. It looks a little different:

dynamic o = new FortranIVObject();

o.ICOUNT = "1";
o.ICOUNT += 4;

Console.WriteLine(o.ICOUNT);

Obviously, I’ve had to replace ExpandoObject with my new type. But you’ll also notice a certain amount of SHOUTING—that’s because my custom dynamic type requires identifiers to be uppercase, to make it feel more like FORTRAN. But with that and the “I” prefix in place, it works: it prints out 5, instead of 14. Rest assured that this only fixes the particular kind of bug encountered by the original snippet—we still get most of the usual danger of dynamic typing, but combined with all the convenience and aesthetic merits of Hungarian notation.

Hanging Chad Emulation

My father went on to tell me of problem he once encountered, relating to FORTRAN IV’s support for using variables without having to declare them:

“The automatic variable creation was an interesting source of errors—a colleague and I once spent a week tracking down a bug due to this. In those days all our source files were on punched cards, and in one place an incompletely punched-out chad had got folded back into place so that what had been a letter “U” became a digit “4” (thanks to how EBCDIC encoded characters on punched cards). So in one place, a variable whose name should have been “TU” became referred to as “T4”. The compiler happily created another variable called “T4” and used it in that statement. So when other parts of the program altered the value of “TU”, this particular line of code was oblivious. Unfortunately, the effect on the behaviour of the program was not obvious (it was doing speed/time calculations to simulate the dynamic performance of a numerically controlled machine tool—System 24), so the program appeared to run OK but the machined parts didn’t come out like they should! So I’m not a big fan of automatic variable creation. I thought that one of the reasons that later languages like PL/1 insisted on you declaring variables explicitly was to avoid just this sort of risk (although more likely caused by typing errors than hanging chads—reminiscent of the election recounts in Florida by which George Bush scraped into the presidency).”

Incidentally, the test/debug round trip time was pretty high, because running updated code involved putting a box of punched cards in a car and driving across London to the nearest IBM data centre. In those days, ‘cloud computing’ referred to braving London’s infamous smog to get to one of the very few places that had a computer.

Some of my readers are doubtless too young to remember that Bush election, and thus way too young to remember punched cards. They’re how we used to store data back before magnetic media became reliable and affordable. A ‘chad’ is a bit of card punched out when ‘writing’ a value by making a hole in the card. Occasionally, the machine punching out the hole didn’t quite cut through the card cleanly, leaving the chad partially attached (or ‘hanging’). Sometimes, a hanging chad would fold back and block up the hole, changing the value that the punched card reader would report next time it saw the card. This is a kind of bit rot, although it’s typically reversible: if you inspect the cards, you can see when a hanging chad has closed up. It’s harder to perform similar media integrity checks through visual inspection with today’s storage devices, sadly.

Anyway, this sort of random renaming of variables struck me as exactly the sort of thing that a dynamic language fan would probably enjoy, so I decided to add some occasional random behaviour to my dynamic object. From time to time, it will act as though the variable name supplied was different from the one the developer intended. Here’s the modified version:

class FortranIVObject : DynamicObject
{
    private readonly Dictionary<string, object> _values =
        new Dictionary<string, object>();

    private readonly Random _chadRand = new Random();

    public override bool TrySetMember(
        SetMemberBinder binder, object value)
    {
        string name = EnsureShoutingAndChadify(binder.Name);

        char first = name[0];
        if (first >= 'I' && first <= 'M')
        {
            value = Convert.ToInt32(value);
        }

        _values[name] = value;

        return true;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        string name = EnsureShoutingAndChadify(binder.Name);

        if (!_values.TryGetValue(name, out result))
        {
            char first = name[0];
            result = first >= 'I' && first <= 'M' ? (object)  0 : 0.0;
            _values[name] = result;
        }

        return true;
    }

    private string EnsureShoutingAndChadify(string name)
    {
        if (name.Any(char.IsLower))
        {
            throw new ArgumentException(
                "FORTRAN identifiers must be UPPERCASE.");
        }

        if (_chadRand.Next(50) == 1)
        {
            var sb = new StringBuilder(name);
            sb[_chadRand.Next(name.Length - 1) + 1] ^= (char) 1;

            return sb.ToString();
        }

        return name;
    }
}

With this modification, my earlier code continues to print 5 as expected most of the time, but once in a while it will print some other value, such as 4 or 0. You don’t get more dynamic than that.

I hope this code is exactly as useful to the community as it deserves to be, and I hope that you enjoy this first day of the month!

Update 2013-04-01 13:31 (GMT+1): fixed bug in the code that handles the I..M for get operations. (I was originally only going to handle that for set.) I put that in at the last minute, with inevitably buggy consequences - the original first code example wouldn't actually compile (because it was a mixture of the original code, and an update to the second snippet), and in both cases, it ended up with a value of 0.0f for both float and integer variables, due to a missing cast.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk