Saturday, September 14, 2013

Contracts (part 2)

In first part I tried to explain Code Contracts through analogy. In this part I will try to talk about compile checks that already existed and they always existed in any strongly typed language.

Writing programs is like writing a law. There are many things which must be taken into account so could our system work optimally. Programs, just like laws, are constantly changing. I always liked to say "introducing new definition(s) in our existing set of definitions". Just like there are many contradictory laws, there are also many contradictory definitions inside any program and these are all compensate by a programmer by writing shortcuts and adding more complexity to system as a whole. For writing a good program you must be a visionary, you must see "clashes" between various definitions (truths) and you must resolve them without creating a shortcuts. But, seeing these "clashes" is very hard, because there could be many new things, which human brain is not capable to comprehend. The only way to see whether you have "clashes" is to evaluate your definitions through many combinations (this process is known in testing as "Code Coverage") and then you can see if your program is in the valid state. The problem with evaluation is - it is slow process if you are doing it manually (this includes writing tests) and on the other hand it is too late to have information at runtime, when your program is already deployed.

The better way (at least in my opinion) for detecting illogical things is to let a compiler warn you about irregularity you just introduced by writing new definitions.

So, writing code with Code Contracts can help you with that, but even without Code Contracts we are still able to constraint ourselves.

Let me start with same example as in previous post:

        public string CutExtension(string text)
        {
              string output = text.Substring(text.Length - 3);

              return output;
        }

OK, we have a function that strips extension from given file name. With Code Contracts we can add preconditions, so we can constraint callers what "type" they'll pass to us. But, even without Code Contracts, we can still create same constraint using new type: 

       public struct FileNameNotNull
       {
              private string _value;

              public string Value
              {
                     get { return _value; }
              }

              public FileNameNotNull(string value)
              {
                     if (value == null)
                     {
                           throw new ArgumentNullException();
                     }

                     _value = value;
              }
       }

Here we defined class which will allow us to wrap condition in its constructor. In this particular constructor we check whether string is null or not. So we can refactor our CutExtension function as follows:

        public string CutExtension(FileNameNotNull text)
        {
            string output = text.Value.Substring(text.Value.Length - 3);

            return output;
        }

Now, we shouldn't care whether null is passed to our function. OK, but we can ask ourselves “but what if FileNameNotNull instance is null?”.  Answer is simple, we are back to the beginning, at least with C#, in C++ we can choose whether to pass parameter as reference or as pointer, but unfortunately we don’t have that luxury in C#. What we can do is to use struct instead, but problem with struct is that it has default constructor so we cannot constraint ourselves as we wanted. So, just for the purpose of example, let’s imagine that we can choose same as in C++ whether we want reference or pointer. In that case, we don’t need to check for null value in the first place, because just like in C++ we can write that next to parameter type with “&” sign.
So our function CutExtension would look like:

        public string CutExtension(string& text)
        {
            string output = text.Value.Substring(text.Value.Length - 3);

            return output;
        }

So we don’t need FileNameNotNull class anymore. But purpose of this blog post is to explain how to leverage type system to constraint our program, so I will emphasize this, handling null value is somehow special case, so we cannot do that on type system level, it must be supported by compiler.
Now let us use type system for other constraints.  In previous part I showed how can we constraint ourselves for string length using Code Contracts. But same can be achieved with type system, we just need a new type:

       public class StringGreaterThan3
       {
              private string _value;

              public string Value
              {
                     get { return _value; }
              }

              public StringGreaterThan3(string value)
              {
                     if (value.Length < 3)
                     {
                           throw new ArgumentException();
                     }

                     _value = value;
              }
       }

       public string CutExtension(StringGreaterThan3 text)
       {
              string output = text.Value.Substring(text.Value.Length - 3);

              return output;
       }

Here we can see, that caller must supply us string with more than 2 characters in length, otherwise he will get exception during construction:

       var parameter = new StringGreaterThan3("33");

       CutExtension(parameter);

Caller will not ever call the CutExtension unction in this case; he will get ArgumentException in StringGreaterThan3constructor. But what if, for instance, we want to check whether given string is also in lowercase, just for the purpose of example, let’s say that our business logic requests that from us:

       public string CutExtension(StringGreaterThan3 text)
       {
              if (text.Value != text.Value.ToLower())
              {
                     throw new ArgumentException("Expected string in lower case");
              }

              string output = text.Value.Substring(text.Value.Length - 3);

              return output;
       }

OK, we added precondition to CutExtension  but now caller should know that, and as I said before, he has several options: to test it, to read documentation, or to read code (if he has it or through reflector).
But, we can change this and push arguments checking to our wrapper function:

       public class StringGreaterThan3
       {
              private string _value;

              public string Value
              {
                     get { return _value; }
              }

              public StringGreaterThan3(string value)
              {
                     if (value.Length < 3)
                     {
                           throw new ArgumentException();
                     }

                     if (text.Value != text.Value.ToLower())
                     {
                           throw new ArgumentException("Expected string in lower case");
                     }

                     _value = value;
              }
       }

Now we have a bit awkward name StringGreaterThan3.  Because it’s clear that it is a lot more. So we can change our class name to something exotic like “StringGreaterThan3AndInLowerCase”. It’s obviously too difficult to comprehend that name, and also, it's too complex code to understand. We replaced simple preconditions checking with new class just because… what? Well, we replaced that just because we don’t want to propagate simple string through many layers. Imagine this - you have some kind of settings class, which is populated by GUI, which then is passed to some engine which works in parallel and uses this information on many places, and what you discover sometimes that someone has put string in your settings class that is not greater than 3 chars and it is not in lowercase, so in case of preconditions directly incorporated in CutExtension function you will get exception about wrong arguments and then you can do what? It is really hard then to decide what to do next. If you are smart enough, you will never catch these exceptions, but reality is, that there are a lot of code out there which is catching everything it can. On the other hand we can push this checking from the point where data is entering from “outside” world and then we can count on compiler - because in rest of the code, we know exactly what are the traits of underlying arguments (in this case string). But once again, wouldn't be great if compiler could generate these types, somehow, invisibly for us, so we can just write our preconditions without writing additional classes for parameters, because it is overwhelming to do it manually. Unfortunately  it cannot, but that’s why we have Code Contracts.
You can imagine like this, whenever you have some precondition, new type will be generated. So, in similar way Code Contracts are able to infer whether you supplied "type" as it was expected. But there’s one thing, how can we compensate Exceptions, I mean, how can we instead of catching exceptions, be warn by compiler that we didn't supplied valid arguments?
Well that definitely must be implemented on the compiler level, or in case of Code Contracts, Static Checker is used. Static Checker will inspect your code for branching and it will check whether you checked for lowercase:

        public string CutExtension(string text)
        {
     Contract.Requires(text != null);
            Contract.Requires(text == text.ToLower());

            string output = text.Substring(text.Length - 3);


            return output;
        }

And if we call this function like this:

            string s = "TEST.tst";
CutExtension(s);

We will get the following error by static checker:
CodeContracts: requires unproven: text != text.ToLower()

And if we add checking before calling the function, then everything is OK. 


string s = "TEST.tst";

     if (s == s.ToLower())
{
         CutExtension(s);
             }

Which is great, no need for exceptions! Static Checker will warn us every time we don’t supply “proven” arguments. That means, for every Contract.Requires(conditionwe write we must have equivalent if (condition)check  from caller side.
But lets extend our example. Let’s say we have one additional function which expects non-null string parameter in lowercase and then it converts it to uppercase:

     public string ToUpperCase(string text)
     {
        Contract.Requires(text != null);
        Contract.Requires(text == text.ToLower());

        return text.ToUpper();
     }

And our example should go like this:

string s = "TEST.tst";

     if (s == s.ToLower())
     {
var res1 = CutExtension(s);
var res2 = ToUpperCase(res1);
}

In this case, Code Contracts will complain about (among other things) that it cannot  prove that input parameter is in the lower case:
CodeContracts: requires unproven: text == text.ToLower()

But why? CutExtension should return string that is proven not null and text is in the lowercase. I know that  Substring call in CutExtension method will not retrieve proven lowercase string, but it doesn't too, because Substring method only affects length property of original string, it doesn't converts characters. So to prove that return type is still in lowercase, we need to write CutExtension method as:

        public string CutExtension(string text)
        {
            Contract.Requires(text != null);         
            Contract.Requires(text == text.ToLower());
            Contract.Ensures(Contract.Result<string>().ToLower() == Contract.Result<string>());

            string output = text.Substring(text.Length - 3);
           
            return output;
        }

So we added Contract.Ensures call, which will ensures that input parameter will have same “properties” when returning same (“derived”) parameter from method! This is a bit overwhelming, because this will introduce code cluttering. But, on the other hand, this is the best we have. Unfortunately, I also think that MS should have had continue developing Spec#, instead of creating new API. I am aware of the fact that they wanted to have this feature available to every .NET language, but on the other hand, code is unnecessary “long”, and not so clean, and above all, I believe it would be much faster, because Code Contract Static Checker is so slow, that compiling will look to you as light speed compering to it...

At the end, here is interesting view on Code Contracts, I must admit, I agree with author on pretty whole thing.

No comments:

Post a Comment