Simulating a guard in c#

Currently i am reading a book about haskell programming and there is one chapter about guards. Because i like the concept of guards a lot i tried to implement one in c#. In this post you can see the result of my efforts. First of all i want to explain what a guard is and how you can implement one in haskell. So a guard is a pattern matching construct. You can define cases and when the overtaken value corresponds with the case then that case is choosen. This is the definition of a guard in Haskell:

guard :: Integer -> String
guard value
  | doublevalue <= 10 && value >= 0 = "small"
  | doublevalue <= 20 && value >= 0 = "middle"
  | doublevalue <= 30 && value >= 0 = "big"
  | otherwise = "not definied"
  where doublevalue = value * 2

If we call the guard with 2 the this:

 guard 2
 small

is the result. In haskell there is the where clause with this clause it is possible define additional values that can be used in the guard. In this case the value gets doubled and this doublevalue is used in the guard. In f# sharp there is also a construct that works like a guard but it is called match pattern. This is the definition of the match pattern (guard) in f#:

let guard v =
    match v with
    | v when v < 10 && v > 0 -> 1
    | v when v = 50 -> 2
    | v when v > 100 -> 3
    | _ -> 0

guard 2
1

I think that guards could also be a practical construct in c#. Here is my implementation of a c# guard like construct. To see if it works i wrote some test cases.

[TestClass]
public class GuardsTests
{
    [TestMethod]
    public void GuardsTestTrue()
    {
        int i = 2;
        var result = i.Match().
            When(v => v < 10, v => 1).
            When(v => v == 50, v => 2).
            When(v => v > 100, v => 3).
            Default(v => 0).Guard();
        Assert.AreEqual(result, 0);

        string value = "test";
        var resultTest = value.Match().
            When(s => s == "not test", s => "not test").
            When(s => s == "test", v => "test").
            Default(v => "no match").Guard();
        Assert.AreEqual(resultTest, "test");

        i = 25;
        int y = 2;
        var doubleresult = i.Match().Where<int>(x => x * y).
            When(v => v < 10, v => 1).
            When(v => v == 50, v => 2).
            When(v => v > 100, v => 3).
            Default(v => 0).Guard();
        Assert.AreEqual(doubleresult, 2);
    }
}

The Guards class is a generic class with two generic types. T is the type of the overtaken value and TResult is the result type. The When method is used to define the cases of the guard, the Default method defines the Default case.

// the Guards class stores the cases that are defined in the when, else and default clauses and
// when the Guard method is called all stored cases are evaluated and the result is calculated.
public class Guards<T, TResult>
{
    private readonly T value;

    // in this list of tuples all defined when cases are stored
    private List<Tuple<Predicate<T>, Func<T, TResult>>> patterns =
        new List<Tuple<Predicate<T>, Func<T, TResult>>>();

    private Tuple<Predicate<T>, Func<T, TResult>> elsePattern = null;
    // in this func the default case is stored
    private Func<T, TResult> defaultPattern = null;
    // in this func the where case is stored
    private Func<T, T> wherePattern = null;

    public Guards(T value)
    {
        this.value = value;
    }
    // every time the when method is called a tuple of a predicate and a result func is stored in 
    // the patterns list
    public Guards<T,TResult> When(Predicate<T> predicate, Func<T, TResult> result)
    {
        patterns.Add(new Tuple<Predicate<T>, Func<T, TResult>>(predicate, result));
        return this;
    }
    // with the where method it is possible to transform the overtaken value before it comes
    // evaluated
    public Guards<T, TResult> Where(Func<T, T> result)
    {
        wherePattern = result;
        return this;
    }

    public Guards<T, TResult> Else(Predicate<T> predicate, Func<T, TResult> result)
    {
        elsePattern = new Tuple<Predicate<T>, Func<T, TResult>>(predicate, result);
        return this;
    }
    // defines the default case
    public Guards<T, TResult> Default(Func<T, TResult> result)
    {
        defaultPattern = result;
        return this;
    }
    // the guard method evaluates the patterns. That means it calls the
    // predicate definied for the pattern and if the pedicate is true it
    // calles the func defined in the tuple. If no pattern matches the predicate
    // then the default pattern is called.
    public TResult Guard()
    {
        if(elsePattern != null)
            patterns.Add(elsePattern);
        if (wherePattern != null)
        {
            var whereValue = wherePattern(value);
            foreach (var item in patterns)
            {
                if (item.Item1(whereValue))
                {
                    return item.Item2(whereValue);
                }
            }
        }
        else
        {
            foreach (var item in patterns)
            {
                if (item.Item1(value))
                {
                    return item.Item2(value);
                }
            }
        }
        if (defaultPattern != null)
            return defaultPattern(value);
        throw new GuardException();
    }
}

The GuardExtensions class definies the Match method that generates a GuardContext object and overtakes the value the Guard should evaluate.

public static class GuardExtensions
{
    public static GuardContext<T> Match<T>(this T value)
    {
        return new GuardContext<T>(value);
    }
}

The GuardContext class defines the connection between the overtaken value that comes from the Match extension method and the Guards object.
// The GuardContext class holds the value that is evaluated
// and is the starting point for the chain of Guards that evaluates the value.
// the GuardContext is nessecary because it has one generic class type T that defines the type of
// the value and the methodes Where, When and Default have the generic TResult type that defines the
// result type of the chaining Methodes. It is the connection between the overtaken value and the Guards object.
// so if the expression:
// string stringResult =
// 10.Match().When(x => x < 10, x => “klein”).When(x => x >= 10, x => “big”).Guard();
// is evaluated the following things happen:
// 1. the extension method Match is called for 10
// 2. in the Match method a GuardContext Object is created and returned
// 3. the when method of the GuardContext Object is called
// 4. a Guards object is created the value is overtaken to this Guard
// and the When method of this object is called
// 5. the predicate and the result func are stored in the patterns collection of the Guards object
// and the Guards object is returned.
// 6. then the When method of the Guards object is executed the predicate and the result func are
// stored in the patterns collection (now the Guard object has two pattern entries).
// 7. The Guard method of the Guard object is called
// and the entries made in the patterns collection are executed with the value
// stored in the Guard object and return a TResult of type string to the stringResult

public classGuardContext<T>
{
    private T value;

    public GuardContext(T value)
    {
        this.value = value;
    }

    public Guards<T, TResult> Where<TResult>(Func<T, T> result)
    {
        var match = new Guards<T, TResult>(value);
        return match.Where(result);
    }
    // the when method generates a Guards object and overtakes the value to this Guards object.
    // After that it executes the When method of the Guards object and starts the evaluation chain also
    // called a Guard.
    public Guards<T, TResult> When<TResult>(Predicate<T> predicate, Func<T, TResult> result)
    {
        var match = new Guards<T, TResult>(value);
        return match.When(predicate, result);
    }
    public Guards<T, TResult> Default<TResult>(Func<T, TResult> result)
    {
        var match = new Guards<T, TResult>(value);
        return match.Default(result);
    }
}

So my self defined Guard in c# is not so beautiful as in haskell or f# but it served its purpose.

Advertisements


If you have a note or a question please write a comment.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s