Nov 01, 2006

A Tour of XAML VIII: More fun with Markup Compatibility

In a previous post about markup compatibility I described how it can be used to enable forward compatibility in XAML documents; documents that take advantage of features in, say, a 2.0 version of the document format but can also be viewed when only the 1.0 version is available. Using a technique similar the previous XAML post, we can also use AlternateContent to get the effect of conditional compilation.

Let say you want to have a label in a dialog that indicates that this is beta software so your customers will not confuse it with your production version. One way to accomplish this is to conditionally compile an XmlnsDefinition attribute into an assembly referenced by the project. When the conditional symbol is defined, the WPF XAML compiler will consider the namespace defined by the XmlnsDefinition attribute as known. If the conditional is not defined, the assembly will not have the XmlnsDefinition attribute and the namespace will be considered unknown. This can be done by adding the following code to a referenced assembly,

#if BETA
[assembly:XmlnsDefinition("BetaVersion", "Example.Technology")]
#endif

Remember, this must be an assembly referenced by the project, not in the project itself (I explain why below).

You can then conditionally include a label, as in,

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
  xmlns:beta="BetaVersion"
  
  ... 
  
  <mc:AlternateContent>
    <mc:Choice Requires="beta">
      <Label>
        This is BETA software. Do not distribute. 
        Use at your own risk.
      <Label>
    </mc:Choice>
  <mc:AlternateContent>
  
  ...
  
<:/Window>

The label will only be included in the beta version of the assembly, not the production version (assuming that BETA is not defined for the production version, a mistake I have made before...). You can even make it more complicated by having other dummy namespaces for other versions and a fallback to catch when the build configuration was not correctly set up, such as,

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
  xmlns:alpha="AlphaVersion"
  xmlns:beta="BetaVersion"
  xmlns:production="ProductionVersion"
  ... 
  
  <mc:AlternateContent>
    <mc:Choice Requires="alpha">
      <Label>
        This is ALPHA software. Confidential.
        Internal use only. Do not distribute
      <Label>
    </mc:Choice>
    <mc:Choice Requires="beta">
      <Label>
        This is BETA software. Do not distribute. 
        Use at your own risk.
      <Label>
    </mc:Choice>
    <mc:Choice Requires="production">
    </mc:Choice>
    <mc:Fallback>
      <Label>
        Franken-build, beware! For test purposes only. 
        Confidential. Internal use only.
        Do not distribute.
        The official build script was not used to 
        produce this assembly.
      </Label>
    </mc:Fallback>
  <mc:AlternateContent>
  
  ...
  
</Window>

I believe this technique is superior to other techniques I have seen described involving data binding and/or triggers because the resulting production version has no runtime overhead for the beta label. The production choice doesn't mention the label at all; the BAML file will not contain any reference to the label and no label will be created at runtime. It is equivalent deleting the entire alternate content block. Also, since it only uses XAML features, not WPF features, it is usable in non-WPF dialects.

If you adopt this technique, I recommend adding the dummy namespaces to an assembly that contains only XmlnsDefinition attributes. Since this assembly doesn't contain any code, the C# compiler will not include it among the references in the final assembly therefore you don't have to distribute it.

The reason you cannot include the XmlnsDefinition in the project itself is because the XAML is compiled before the assembly is built (most of the time, I will get into the details of this in a later post). The compiler cannot know what attributes are in the project until the project's assembly has been built. Since the BAML file needs to be in included in the assembly, it must be compiled before the assembly. This is a catch-22 so the compiler ignores XmlnsDefintiion attributes in the project assembly but will respect attributes in referenced assemblies (which are built before the XAML is compiled).

In a future version of XAML we might consider adding formal support for conditional compilation but, for now, you can get the effect of conditional compilation through the use of AlternateContent, even if it is a bit indirect.

Oct 25, 2006

The Expression Problem: The JRuby Interpreter

I previously discussed the expression problem could be solved with various patterns including the visitor pattern and using a procedural approach. It is interesting to note that JRuby used to use a visitor pattern but switched to a procedural approach to improve performance. You can read about it here. This is a side note in the article since what they want to do is convert Ruby to run as byte code similar to how JPython works.

Oct 13, 2006

XAML Part VII: Fun with markup compatibility

If you misspell an attribute name in an HTML document the browser will simply ignore it. For example, if you spell the href attribute as zhref the <a> tag the attribute will be ignored. For example, this is text is in such an <a> tag. It is displayed like a link but it doesn't go anywhere because it doesn't contain an href attribute. Intentional misspellings can be used to temporarily comment out an attributes or an element. XAML doesn't support this technique directly. If you misspell an attribute or an element the XAML parser will emit an error message and refused to produce a BAML file or load the document dynamically. This is the behavior you want if you unintentionally misspelled an attribute. The compiler will tell you exactly where the error is and you can quickly find and fix the problem. But temporarily commenting out an attribute can sometimes be useful when experimenting or prototyping. XAML provides a way to duplicate the this commenting out technique through the use of the markup compatibility namespace. Markup compatibility tells the parser which namespaces are not required to be recognized when loading a XAML document. If you declare a namespace that will never be recognized, such as "Comment", you can use that namespace to comment out items by using its prefix. For example, let say you want to see the effect of removing the Width property of a Button but not lose the value it currently has which would happen if you simply deleted it. Consider the following,

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> 
  <Canvas>
    <Button Canvas.Top="50" Canvas.Left="50" Width="100" Height="50">
      Hello
    </Button>
  </Canvas>
</Window>

To comment out the Width attribute you can add the markup compatibility namespace, then an unknown namespace, and finally declare the unknown namespace ignorable. This might look like,

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:c="Comment"
    mc:Ignorable="c">
  <Canvas>
    <Button Canvas.Top="50" Canvas.Left="50" Width="100" Height="50">
      Hello
    </Button>
  </Canvas>
</Window>

You can then comment out the Width and Height attributes,

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:c="Comment"
    mc:Ignorable="c">
  <Canvas>
    <Button Canvas.Top="50" Canvas.Left="50" c:Width="100" c:Height="50">
      Hello
    </Button>
  </Canvas>
</Window>

You can even comment out the entire Button with,

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:c="Comment"
    mc:Ignorable="c">
  <Canvas>
    <c:Button Canvas.Top="50" Canvas.Left="50" Width="100" Height="50">
      Hello
    </c:Button>
  </Canvas>
</Window>

I only recommend this when you are prototyping. You shouldn't have commented out text in production XAML files. Fortunately, this technique makes it easy to determine if you have removed all such commented out items. Simply delete the xmlns:c and mc:Ignorable declarations from Window. The compiler will now generate and error if you haven't removed comment prefixes. Finally, this technique makes it clear to any reader (both mechanical and human) that you intended to comment out this property, you simple didn't just misspell it.

Sep 25, 2006

Linked List VI: Recursion - see recursion

I finally have a definitive answer to the question of why MyNode is needed. Consider the following, simplified, version of the LinkedList classes I posed,

  class C<X> { class N { } }
  class D: C<D.N> { }

This is really an obfuscated form of

  class N<X> { }
  class C<X> { }
  class D: C<N<?>> { }

where ? needs to be replaced by an infinite list if N<N<...>>. In other words, I am trying to define a type directly in terms of itself, a T where T=N<T>. This is not allowed. An additional type is required to break the cycle, such as M in,

  class M: N<M> { }

Then I can write,

  class D: C<M> { }

So, there we have it, MyNode is necessary to avoid defining a type directly in terms of itself. The unobfuscated version makes this much more clear. MyNode serves the same role as M does above.

Thanks again to Andrew for taking the time to beat this into my thick skull.

Sep 19, 2006

Fatherly Advice To New Programmers

It looks like none of my children will become programmers. Instead of letting my fatherly advice to my new programmer son or daughter go to waste, I am going to inflict it on you. If you are newly embarking on the journey that is becoming a programmer, here is advice your father would tell you if he were a programmer. These are things I had to learn the hard way.

Keep Learning: Read. Go to conferences. Subscribe to journals. Take classes. Whatever it takes for you to keep learning, make it a priority. Learn about every language you can find. Take time to learn about any new frameworks, algorithms, techniques, models, paradigms, you can. Each gives you one more tool in your tool chest. Each will help you more easily tackle your next programming problem. Find a mentor, someone much better than you, and learn all they can teach you. Never stop learning.

Learn To Communicate: I often joke that the most important skill you can learn as a programmer is how to draw a rectangle on a white-board. Communication is critical to the job of a programmer. Communicating with customers, clients, users, co-workers, bosses, vice presidents, CEO's, board-members, VC capitalists, all will become important at some point in your career. Learn how to speak in public. Learn how to write in English. Learn to effectively communicate in person. Learn how to persuade without shouting, getting angry, or getting flustered. Learn how to speak without jargon. Help people understand what you are doing. Learn to break things into simple, understandable pieces. Learn to communicate by analogy and symbolism. Learn to communicate.

Be Predictable: Learn how fast you can comfortably program. Wait to predict how long it will take you to complete a task until you understand it. Allow for the unexpected. Plan for vacations and time-off. Live with your predictions. I don't believe I know a problem well enough to predict how long it will take to complete until I can break that task down into sub-tasks that each take no longer than 3 days (often less than one day). Live by this rule, under-promise, over-deliver. It is better to deliver in 10 days what you promised in 15 than to deliver in 10 days what you promised in 5. People depend, schedule, and plan around your predictions. Make them the best you can and make sure you can comfortably do them or you will be asked to live up to your uncomfortable predictions. You will not be good at it at first; to compensate, verify your predictions with someone more experienced. Learn to get better. Be predictable; other depend on you.

Own Up To Your Mistakes: You will make mistakes. How you handle your mistakes is how you will be judged. Learn how to say "I was wrong." If you underestimated how long it will take you to do something, tell people as soon as it is clear to you. If you broke the build, fix it. If you created a bug, fix it. Don't deny the mistake, don't make excuses for the mistake, don't figure out how to hide the mistake, don't blame others for the mistake, do something about it. Take ownership of your mistake or you will repeat it.

Never Let Bad Code Off Your Desk: Your job as a programmer is to write code that works, never let code off your desk you are not sure meets that criteria. Not only does it reflect badly on you, it is much more expensive, and much harder, to find a problem once it leaves your desk than before. Learn to love unit tests. Learn to love code coverage. Learn to test your code better than people who are paid to test it. Be embarrassed about bugs that are found after you have checked-in. Be especially embarrassed when a customer finds the bug. Don't rely on others to find your bugs for you, find them and fix them yourself. Don't hope it will work. Test it. Don't assume it will work. Test it. Don't whatever. Just test it. If you haven't tested it, it doesn't work; of this you can be sure. But, even if you are diligent with testing, bugs will get by you. You will make mistakes but try your best not to.

Programming is Fun But Shipping is Your Job: Programming is fun. It is the joy of discovery. It is the joy of creation. It is the joy of accomplishment. It is the joy of learning. It is fun to see your handiwork displaying on the screen. It is fun to have your co-workers marvel at your code. It is fun to have people use your work. It is fun have your product lauded in public, used by neighbors, and discussed in the press. Programming should be fun and if it isn't, figure out what is making it not fun and fix it. However, shipping isn't fun. I often have said that shipping a product feels good, like when someone stops hitting you. Your job is completing the product, fixing the bugs, and shipping. If bugs need fixing, fix them. If documentation needs writing, write it. If code needs testing, test it. All of this is part of shipping. You don't get paid to program, you get paid to ship. Be good at your job.

Remember these simple statements,

  • Never stop learning.
  • Communication is critical.
  • Under promise, over deliver.
  • "I was wrong."
  • If it is not tested it doesn't work.
  • Programming isn't your job, shipping is.

Sep 08, 2006

Linked List V: Revenge of the Type Parameter

I honestly thought I was done with this topic but as it turns out I missed something. I stated firmly that "currently, in C#, we are stuck with the cast" but I was wrong. One of the great things about working at Microsoft is the people you can interact with. In back burner researching a definitive answer to Hallvard's question to my post of why linked list LinkedList<T> cannot be written,

  class LinkedList<T>: LinkedList<T, LinkedList<T>.Node> { }

I asked Andrew Kennedy to look at my page. I like talking to Andrew; I learn at least one new thing every time we talk. This was certainly no exception. He quickly pointed out something that was staring me squarely in the face but I missed entirely, folding my implementation of DoubleLinkedList<T> with DoubleLinkedList<T, N> and closing the node type directly will remove the cast. In other words, DoubleLinkedList<T> can be written without casts as,

    class DoubleLinkedList<T>: LinkedList<T, DoubleLinkedList<T>.DoubleNode>,
        IDoubleLinkedList<T> {

        public class DoubleNode: Node {
            DoubleNode _previous;

            public override DoubleNode Next {
                get {
                    return base.Next;
                }
                set {
                    base.Next = value;
                    value._previous = this;
                }
            }

            public DoubleNode Previous { get { return _previous; } }
        }

        public IEnumerable<T> Backwards {
            get {
                if (Last != null) {
                    DoubleNode current = Last;
                    do {
                        yield return current.Data;
                        current = current.Previous;
                    } while (current != Last);
                }
            }
        }
    }

which closes the node parameter instead of leaving it open. This is one of those head-slapping, "how stupid of me" moments like when someone points out that "you know, if you would have pushed the other pawn instead, you would have had checkmate in three moves." You can see that this is a straight forward application of the requires clause version (declaring it to be DoubleNode instead of requiring it). In my zest to introduce the concepts of the "this type" from LOOM and the requires clause from Scala, I missed what should have been obvious. Silly me.

This solution has the limitation that you cannot derive something like TripleLinkedList<T> from DoubleLinkedList<T> so my example still holds some water (TripleLinkedList<T, N> can be derived from DoubleLinkedList<T, N>) but it is not nearly so compelling. I still think that one of the two features would be very useful but I need a much better motivating example.

While I still don't have a definitive answer to Hallvard's question, I picked up this gem along the way. Not bad so far.

Sep 07, 2006

August CTP of Cider

Another month and another version of Cider. The August CTP is now available here. You can read the release notes and so forth from Channel 9 here. Not only did we make substantial changes, a list of which can be found here, we also updated it to work with the new .NET Framework 3.0 RC1 available here.

OK, I know we released a CTP in July I didn't blog about, but it was substantially a refresh to the CTP available for June (and I am a lazy blogger). Yes, yes, I also realize it is September and not August. I am blogging about it in September because it has just become available. The reason we call it the August CTP and not the September CTP is complicated but you can probably guess at least one reason...

BTW, new and improved versions of stuff we removed from the main tree are slowing winding their way back. I will be a bit coy about them now so as not to steal the thunder away from the current CTP but some cool and exciting things are still to come.

As always, we really want your feedback so be sure to check out the discussion site here.