Monday, April 07, 2008 2:00 AM bart

Pattern Matching in C# - Part 1

In the previous episode of this series we took a look at the concept of pattern matching as it exists in a couple of functional languages out there as well as how we're going to map it to C# 3.0 syntax. We left off with the exercise of guesstimating the public contract of our Pattern`1 class based on this sample:

string message = new Pattern<string>(x)
   .Match((string name) => new Person(name, 25), name => name + " is 25 years.")
   .Match((int age) => new Person("John", age), age=> "John is " + age + " years.")
   .Else(() => "Unmatched object");

 

Deriving the pattern

A few observations to infer this contract:

  1. The type parameter T of Pattern<T> denotes the return type of the pattern.
  2. Patterns can operate on any object, hence the constructor takes a System.Object.
  3. The number of matches, denoted by Match calls, has no upper bound and we want to chain these together fluently (aka fluent interfaces, just like LINQ does with its Standard Query Operators).
  4. There can only be one Else clause and it seems to return the pattern's result type (we'll revisit this decision in subsequent posts).

So, we already have something like:

class Pattern<T>
{
   public Pattern<T>(object o) { ... }

   public Pattern<T> Match(...) { ... }
   public T Else(...) { ... }
}

The way we'll chain together match clauses is by accumulating information about matches in the pattern and returning ourselves, until Else comes around to complete the pattern. Notice different decisions could be made for the Else policy, we stick with the above for now for sake of simplicity.

There's one big missing piece: the parameterization of the Match and Else methods. Given the sample code, you're seeing lambdas everywhere but from that observation alone one can't make the decision of the type. Lambdas in C# 3.0 don't really have a type by themselves, they can be assigned to two things: either delegates (that compile into IL) or expression tree objects (that compile into expression trees = code as data). A quick analysis:

  • For the match clause, the first parameter to Match that is, we don't really want to new up an object like new Person(bar, 25). Sure enough, we could trace back (see further) the 25 to the Age property and check the passed in object is of type Person with a matching Age property value, but what for heaven's sake would we pass in to the "free parameter" denoting the person's name? Moreover, we need to be able to extract the Name property instead.
  • For the match body, once we have the "free parameter(s)" in case the match clause evaluated positively, we need to execute the body given the value for those parameters. One question is what precisely we want to allow in the body. One could argue simply for valued expressions, since the whole thing returns a value. On the other hand, statements would come in handy too (you could imagine a Pattern class without a return type, i.e. a non-generic variant), much like a normal switch statement has statement bodies in the case blocks.

The latter one is fairly straightforward at the time of writing this post. We don't have statement trees in the language (yet) and since we want to allow arbitrary code - not just simple expressions - the use of a Func<...> delegate is the only way. This is where our syntactical restriction comes from to a certain extent, as I'll outline below.

For the former one, we're in somewhat more trouble it seems. We don't really need to create an object in the match clause, we rather need to match it and have all the information required to do so. This screams for a runtime approach since we're not changing the language nor the compiler. Such a meta-way of programming is enabled through expression trees so the match clause will be of type Expression<Func<...>>.

Here's the full definition finally:

class Pattern<T>
{
   public Pattern<T>(object o) { ... }

   public Pattern<T> Match<TR>(Expression<Func<TR>> e, Func<T> f) { ... }
   public Pattern<T> Match<T1, TR>(Expression<Func<T1, TR>> e, Func<T1, T> f) { ... }
   public Pattern<T> Match<T1, T2, TR>(Expression<Func<T1, T2, TR>> e, Func<T1, T2, T> f) { ... }
   public Pattern<T> Match<T1, T2, T3, TR>(Expression<Func<T1, T2, T3, TR>> e, Func<T1, T2, T3, T> f) { ... }
   public T Else(Func<T> f) { ... }
}

with a limited number of overloads since we don't have a params equivalent for type arguments as captured in the following PC# syntax:

   public Pattern<T> Match<params T[], TR>(Expression<Func<params T[], TR>> e, Func<params T[], T> f) { ... }

 

Syntax restrictions

As outlined before, the syntax of our match is a bit cumbersome in that we need to repeat the parameterization for both the match clause and match body:

.Match((string name) => new Person(name, 25), name => name + " is 25 years.")

There are different approaches possible but keep in mind we need the former to become an expression tree and the latter a regular delegate. You could imagine a Match object defined like this instead:

class Match<T1, TR>
{
   public Expression<Func<T1, TR>> Clause { get; set; }
   public Func<T1, T> Body { get; set; } // Q: Where could T possible come from?
}

while having and Add method on Pattern<T>, taking in a Match<...> object and allowing for collection initializer syntax. Now we could new up the match using either a constructor (not shown above) or object initializer syntax but we still need to say the parameterization twice (i.e. two lambdas). What we'd need is lifting of the parameterization to both properties:

(string name) => new Match { Clause = new Person(name, 25), Body = name + " is 25 years." }

Exercise: I'll leave it to the reader as an intellectual journey to think about the issues that arise in this hypothetical code and how something maybe could be done if you restrict the bodies to be expressions only.

For our explorative purposes in this series we consider the syntax restrictions not the biggest concern.

 

Picking an evaluation strategy

Next question to answer is how we're going to evaluate the pattern match. We'll explore different methodologies but here's a list of possibilities that jump into our minds:

  1. As the chain of Match calls flows, we evaluate the match body at once and if it evaluates we run the body. The result is kept around and subsequent Match calls short-circuit any subsequent match clause evaluation. If the flow reaches the Else clause and the pattern hasn't taken on a value yet, we evaluate the Else body and return that as the result. This is a form of eager evaluation.
  2. Instead of trying to match straight away, we keep track of the match clauses and bodies and store those somewhere. This is a form of lazy evaluation but it allows for more flexibility since we can reuse the same Pattern object multiple times and can even come up with an evaluation strategy.

We keep the best for the last and start off with eager evaluation in today's post. In subsequent posts we'll revamp the Pattern<T> class to support lazy evaluation. Be prepared for tons of cool stuff including Reflection.Emit by then...

 

The art of unification

In the center of our pattern matching lies the evaluation of the match clauses which comprises two tasks:

  • Checking for a match to the supplied object. This is called unification, a technique also employed in languages like Prolog.
  • Extracting unmatched "free parameters" (aka match variables) in order to feed those into the match body delegate.

What's there to unify? Lots of stuff and we could go a long way in providing flexibility. Let's start with a basic sample:

int Fib(uint i)
{
   return new Pattern<uint>(i)
      .Match(() => 0, () => 1)
      .Match(() => 1, () => 1)
      .Match((uint n) => n, n => Fib(n - 1) + Fib(n - 2))
      .Else(() => 0 /* should never happen */ );
}

Notice we could work with an int too but then we need to take care of the negative case. This would require guard conditions (like when in F#) which is an easy extension to the pattern match code (exercise for later). The code above already illustrates a predictable issue with eager evaluation: since we call ourselves recursively, we always new up the same pattern object which is clearly not the best idea performance-wise. As we'll see later by keeping the pattern object around for reuse we can improve the situation a lot, and we'd even come close to the imperative variant:

int Fib(uint i)
{
   if (i == 0) return 1;
   else if (i == 1) return 1;
   else return Fib(i - 1) + Fib(i - 2);
}

which could even be written as a nested ternary operator expression:

int Fib(uint i)
{
   return (i == 0 ? 1 : (i == 1 ? 1 : Fib(i - 1) + Fib(i - 2)));
}

Back to the original question, what's there to unify in this case? Consider the following match clause: () => 0. In terms of an expression tree this will result in a LambdaExpression with it's Body set to a ConstantExpression, wrapping the value 0 . This means our translated match looks like (pseudo):

if (<inputobject> == 0)
   // execute body

Nothing too fancy and actually precisely the same as a regular switch expression that matches a constant. So, let's skip this trivial case (exercise: implement this unification yourself extending the code presented further on - the third match with variable n will be covered when talking about the extraction phase).

What's more interesting is the case of matching a complex type, let's call it a record, as in:

string message = new Pattern<string>(x)
   .Match((string name) => new Person(name, 25), name => name + " is 25 years.")
   .Match((int age) => new Person("John", age), age=> "John is " + age + " years.")
   .Else(() => "Unmatched object");

Consider the first match clause:

(string name) => new Person(name, 25)

One thing is clear: we need to do a type-check for a Person object. A simply equality check won't work since we don't have a value to compare with (name is unspecified). However, we're missing an additional piece of information. All we have is a constructor call which is turned into an expression tree like this:

ParameterExpression name = Expression.Parameter(typeof(string), "name");
LambdaExpression<Func<string, Person>> clause = Expression.Lambda(
   Expression.New(
      typeof(Person).GetConstructor(new Type[] { typeof(string), typeof(int) }),
      name,
      Expression.Constant(25)
   )
);

The culprit here is the second parameter to the constructor call in the NewExpression. We know its value to be 25 but given a random Person object, how can we correlate this value back to "something" to check against? We know the parameter denotes the Age property and we assume what has been passed through the constructor can be retrieved - unchanged - through the Age property but our pattern matcher doesn't know this. Remember we defined the Person object in PC# in the previous post:

class Person(string Name, int Age)

Consider this a record type with two properties Name and Age and a constructor that sets both, just as we proposed in the following C# 3.0 translation:

class Person
{
   public Person(string name, int age)
   {
      Name = name;
      Age = age;
   }

   public string Name { get; private set; }
   public int Age { get; private set; }
}

Notice the immutability employed by the properties by having a private setter. However, the translation above alone isn't enough since we only have a local view on the situation through the ConstructorInfo object which doesn't know anything about properties. In order to correlate things back, we'll add some metadata on the constructor:

   public Person([Property("Name")]string name, [Property("Age")]int age)
   {
      Name = name;
      Age = age;
   }

Now, the ConstructorInfo object emitted in the expression tree will carry enough information to trace back the second parameter to the Age property and emit the following (idealized) check for the match:

Person p;
if ((p = <inputobject> as Person) != null && p.Age == 25)
   // execute body

To keep things simple, we'll only support one level of unification but an extension to this isn't too hard either (it just takes some implementation effort with some recursion). Such a scenario arises when you do a match like this:

(string name, string city) => new Product(name, 123.45, new Supplier(city, "BE"))

Freaks could take a detour from here through the world of constructor algebras but that would lead us off-track for the scope of this post.

There's one piece of low-hanging fruit left: object initializers which get mapped onto MemberInitExpression. For example, consider the following match clause:

(string name) => new Person { Name = name, Age = 25 }

This maps to an expression tree like this:

ParameterExpression name = Expression.Parameter(typeof(string), "name");
LambdaExpression<Func<string, Person>> clause = Expression.Lambda(
   Expression.MemberInit(
      Expression.New(typeof(Person)),
      Expression.Bind(typeof(Person).GetProperty("Name").GetSetMethod(), name),
      Expression.Bind(typeof(Person).GetProperty("Age").GetSetMethod(), Expression.Constant(25))
   )
);

Unfortunately, the property's setter is kept instead of the property itself, so you'd need to map it back to the property itself (a substring operation on the name to cut off the "set_" part will do it). A bigger issue is we'd have to loosen our definition of a record type since the setter obviously needs to be accessible in order for the object initializer to compile (in this case to an expression tree). We'll leave the processing of the low-hanging fruit to turn it into a pattern match tropical cocktail as an exercise to the reader.

 

Binding leftover parameters

We said before pattern matching embodies two tasks: the unification procedure to derive the match condition but also the extraction of remaining parameters that are to be fed in to the match body. In our running sample

.Match((string name) => new Person(name, 25), name => name + " is 25 years.")

let's assume we're processing <inputobject> = new Person("Bart", 25). We already saw this is turned into:

Person p;
if ((p = <inputobject> as Person) != null && p.Age == 25)
   // execute body

However, to execute the body, we need to extract the value for name first. Following the same metadata-path as described before, we trace the name parameter to the constructor back to the Name property, so the execution code becomes:

Person p;
if ((p = <inputobject> as Person) != null && p.Age == 25)
   <result> = <body>(p.Name);

This completes our match operation translation.

 

Coding time - A first attempt

Let's get into some coding and start with the skeleton. We're going to implement a fluent interface that continues to try finding a match and as soon it has found one, the Match and Else clauses should be a no-op. In other words, the lifecycle of a Pattern<T> object goes from valueless to valued with Else being the last change of making such a transition. Therefore we adapt the _hasValue pattern. In addition to this we keep to keep track of the result and the input, leading to the following:

class Pattern<T>
{
   private bool _hasValue;

   public Pattern(object o)
   {
      Object = o;
   }

   public object Object { get; private set; }
   public T Result { get; private set; }

   ...
}

Notice I'm keeping the result in a publicly available property, although it could be kept around in a private field. However, when we switch from eager to lazy evaluation, we'll see that the Else clause shouldn't return the result of the match. Instead we'll want to trigger the evaluation manually on a pre-constructed pattern matching object. There the public Result property will come handy as an escape valve for cases where we still want eager evaluation, triggered by calling Result. This allows us to share the same interface for both lazy and eager evaluation, but more on that later.

Next, we'll put our public contract for Match and Else in place:

   public Pattern<T> Match<TR>(Expression<Func<TR>> e, Func<T> f)
   {
      return MatchInternal(e, f);
   }

   public Pattern<T> Match<T1, TR>(Expression<Func<T1, TR>> e, Func<T1, T> f)
   {
      return MatchInternal(e, f);
   }

   public Pattern<T> Match<T1, T2, TR>(Expression<Func<T1, T2, TR>> e, Func<T1, T2, T> f)
   {
      return MatchInternal(e, f);
   }

   public Pattern<T> Match<T1, T2, T3, TR>(Expression<Func<T1, T2, T3, TR>> e, Func<T1, T2, T3, T> f)
   {
      return MatchInternal(e, f);
   }

   public T Else(Func<T> f)
   {
      ...
   }

By flowing all matches through a common MatchInternal method we can add overloads at will without any relevant effort (although you'll run out of Func objects, so if you want more than 5 parameters, you'll have to brew your own function delegates, which is also trivial).

Exercise: Before moving on (or scrolling down), guess the signature of MatchInternal that establishes its generic nature.

Time to enter the real magic of the pattern match's mechanics. Here's what MatchInternal looks like, nicely broken down into the distinct phases of an individual match: unification with parameter extraction and body evaluation in case the match succeeds:

private Pattern<T> MatchInternal(LambdaExpression e, Delegate f)
{
    //
    // Already matched.
    //
    if (_hasValue)
        return this;

    //
    // List of bindings.
    //
    Dictionary<ParameterExpression, PropertyInfo> bindings;

    //
    // Try to find a match.
    //
    if (!TryMatch(this.Object, e, out bindings))
        return this;

    //
    // We have a match. Evaluate the match body.
    //
    this.Result = Evaluate(this.Object, e, f, bindings);
    _hasValue = true;

    //
    // Allow further chaining (won't do anything because of _hasValue check).
    //
    return this;
}

The logic is straightforward: _hasValue acts as a barrier so that only one match clause can be executed. TryMatch does the unification work, producing a list of bindings in case the match was valid. In that case, evaluation of the body kicks in and the result value is set. In any case, we pass on ourselves to allow the fluent interface pattern. As in our type switch before, there are alternatives to implement such a pattern and making it flow with all sorts of objects (hint: extending System.Object) but let's no go there (although it's applicable under certain dynamic circumstances).

Our TryMatch match implements the unification algorithm. For sake of simplicity I've trimmed down the implementation to the bare minimum, only supporting non-nested NewExpressions. Extending it to support ConstantExpression and MemberInitExpression isn't too hard and more fancy matches (tip: lists, dictionaries, etc using collection initializers) are possible too. Stay tuned to see more patches in further posts, but for now, here's our minimalistic approach:

private static bool TryMatch(object o, LambdaExpression e, out Dictionary<ParameterExpression, PropertyInfo> bindings)
{
    bindings = new Dictionary<ParameterExpression, PropertyInfo>();

    //
    // Only support for NewExpression.
    //
    NewExpression ne = e.Body as NewExpression;
    if (ne == null)
        throw new NotSupportedException("Match clause can only contain NewExpression.");

    //
    // Created target type.
    //
    Type target = ne.Constructor.DeclaringType;

    //
    // Definite mismatch if input object isn't (a subclass of) the constructed type.
    //
    if (!target.IsAssignableFrom(o.GetType()))
        return false;

    //
    // Resolve parameters.
    //

    int i = 0;
    foreach (ParameterInfo param in ne.Constructor.GetParameters())
    {
        //
        // Mapping information from constructor parameters to properties required.
        //

        PropertyAttribute pa = param.GetCustomAttributes(typeof(PropertyAttribute), false).Cast<PropertyAttribute>().SingleOrDefault();
        if (pa == null)
            throw new InvalidOperationException("Input object doesn't have required mapping information.");

        //
        // Find the property.
        //

        PropertyInfo property = target.GetProperty(pa.Name);
        if (property == null)
            throw new InvalidOperationException(String.Format("Property {0} on type {1} not found.", pa.Name, target.Name));

        ConstantExpression ce;
        ParameterExpression pe;

        //
        // Expression should either be a constant or a parameter reference.
        //
        Expression arg = ne.Arguments[i++];
        if ((ce = arg as ConstantExpression) != null)
        {
            //
            // Check the object's property matches the constant.
            //
            object value = property.GetValue(o, null);
            if (!Object.Equals(value, ce.Value))
                return false;
        }
        else if ((pe = arg as ParameterExpression) != null)
        {
            //
            // Keep track of the loose parameter.
            //

            bindings[pe] = property;
        }
        else
            throw new NotSupportedException("Can only match constants.");
    }

    return true;
}

Let's analyze piecemeal. Apart from some boilerplate input checking because of our intended aforementioned limitation, the first real check is this one:

    //
    // Created target type.
    //
    Type target = ne.Constructor.DeclaringType;

    //
    // Definite mismatch if input object isn't (a subclass of) the constructed type.
    //
    if (!target.IsAssignableFrom(o.GetType()))
        return false;

This simply checks that the object passed in to the pattern match is assignable to the type used in the match clause. We allow subtypes to match a supertype, which makes sense if you have extended record types like Genious : Person where all of the base class's properties are still set by the subclass's constructor (and even that isn't really required).

Next we enter the parameter resolution loop which analyzes all of the constructor's parameters:

    //
    // Resolve parameters.
    //

    int i = 0;
    foreach (ParameterInfo param in ne.Constructor.GetParameters())
    {
        ...
    }

This is the real unification engine at work. First, using some LINQ operators, there's a lookup for the constructor parameter metadata. Without this we can't map back constructor parameters to the underlying properties to do either matching or value extraction. For our record types we expect this metadata to be present.

        //
        // Mapping information from constructor parameters to properties required.
        //

        PropertyAttribute pa = param.GetCustomAttributes(typeof(PropertyAttribute), false).Cast<PropertyAttribute>().SingleOrDefault();
        if (pa == null)
            throw new InvalidOperationException("Input object doesn't have required mapping information.");

Having the metadata is one thing, it should be valid too:

        //
        // Find the property.
        //

        PropertyInfo property = target.GetProperty(pa.Name);
        if (property == null)
            throw new InvalidOperationException(String.Format("Property {0} on type {1} not found.", pa.Name, target.Name));

Last but not least, the matching itself. Ideally, the world would be split into just two pieces over here: ParameterExpressions denote extractions, while all other types of expressions would denote match conditions (possibly recursively). To keep things simple, we only unify with constants in a non-recursive way, hence this code:

        ConstantExpression ce;
        ParameterExpression pe;

        //
        // Expression should either be a constant or a parameter reference.
        //
        Expression arg = ne.Arguments[i++];
        if ((ce = arg as ConstantExpression) != null)
        {
            //
            // Check the object's property matches the constant.
            //
            object value = property.GetValue(o, null);
            if (!Object.Equals(value, ce.Value))
                return false;
        }
        else if ((pe = arg as ParameterExpression) != null)
        {
            //
            // Keep track of the loose parameter.
            //

            bindings[pe] = property;
        }
        else
            throw new NotSupportedException("Can only match constants.");

Starting with the ParameterExpression, such a match simply results in adding a binding that keeps the metadata for the property set by that constructor parameter. We'll use this in the Evaluate method to extract data before feeding it into the match body. The remainder case is the ConstantExpression (due to intentional limitations outlined before), so we simply extract the value from it and check for equality with the value of the underlying mapped property. The way of doing the equality check could be part of a separate discussion but Object.Equals is solid enough for our goals.

Exercise: Could you think of a way to allow wildcard matches (as _ in F#)? Would MemberInitExpression help to solve the problem? What if you have to provide the capability with NewExpressions having no constructor overloads (tip: Unmatched<T> type)? Those of you familiar with old dragons like SASL might thunk of something else too.

With that, our unification process has completed. Let's move on to the evaluation phase.

private static T Evaluate(object o, LambdaExpression e, Delegate f, Dictionary<ParameterExpression, PropertyInfo> bindings)
{
    ParameterInfo[] targetParameters = f.Method.GetParameters();
    object[] args = new object[e.Parameters.Count];
    int j = 0;
    foreach (ParameterExpression param in e.Parameters)
    {
        //
        // Parameters need to be bound in the match expression.
        //
        PropertyInfo property;
        if (!bindings.TryGetValue(param, out property))
            throw new InvalidOperationException("Parameter " + param.Name + " was not bound in the pattern match.");

        //
        // Check type of value retrieved from target property against the expected type from the parameter.
        //
        object value = property.GetValue(o, null);
        if (!value.GetType().IsAssignableFrom(param.Type))
            throw new InvalidOperationException(String.Format("Property {0} on type {1} cannot be bound to parameter {3}.", property.Name, property.DeclaringType.Name, param.Name));

        args[j++] = value;
    }

    //
    // Invoke match body lambda and assign result.
    //

    T result = (T)f.DynamicInvoke(args);
    return result;
}

First of all, notice we've lost static type information on the parameters of the match body due to our generic approach of having one Evaluate-fits-all. If the user of the object doesn't trick the compiler by introducing casts on the action body, we should be safe. Since we're all paranoid framework-writers, I've added some additional checking for consistency by doing a simple type compatibility check comparing the property's value's type against the target parameter in the action body delegate.

The essence of this code is to iterate over the parameters in the match clause, retrieve the binding information gathered during the unification phase, extract the property values and feed them into the match body delegate. For now, we please ourselves with the crude approach of putting everything in an array and calling DynamicInvoke on the delegate (which is an expensive operation). Ultimately we get the result which should be compatible with the result type T (unless the user tricked the compiler by introducing a cast on the match body delegate - it's straightforward to implement another type consistency check here).

This leaves us with one remaining method: Else. Else doesn't take parameters (tip: you can still access the original object in the Else match body thanks to closures) and simply evaluates if no result has been found yet:

public T Else(Func<T> f)
{
    if (!_hasValue)
    {
        _hasValue = true;
        Result = f();
    }

    return Result;
}

That's it! I said it was trivial, didn't I?

 

Putting it to the test

Let's start with a simple collection of objects:

var people = new List()
{
   new Person("Bart", 25),
   new Person("John", 52),
   new Person("Lisa", 25),
   new Person("Rosa", 63),
   "Hello"
};

Since patterns are valued expressions, we can use them e.g. in a LINQ projection clause:

var res = people.Select(p => new Pattern<string>(p)
    .Match((string name) => new Person(name, 25), name =>
    {
        return name + " is 25.";
    })
    .Match((int age) => new Person("John", age), age =>
    {
        return "John is " + age + ".";
    })
    .Match((string name, int age) => new Person(name, age), (name, age) =>
    {
        return "I'm matching them all!";
    })
    .Else(() =>
    {
        return p.ToString();
    })
);

foreach (var s in res)
    Console.WriteLine(s);

And here's the result:

image

Agreed, it's a little verbose but hey, it's only (oh so important) syntax. What's more interesting for this post is to show off the capabilities of expression trees beyond the LINQ case. Next time, we'll switch gears and move on to lazy evaluation. We'll take a look at performance to motivate our lazy evaluation adventure (you can already guess how the above performs).

 

Happy matching!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: , ,

Comments

# re: Pattern Matching in C# - Part 1

Monday, April 07, 2008 2:58 AM by Steve

Awesome, can't wait for the next part :)

# mind mapping &raquo; Blog Archive &raquo; Pattern Matching in C# - Part 1

Pingback from  mind mapping  &raquo; Blog Archive   &raquo; Pattern Matching in C# - Part 1

# re: Pattern Matching in C# - Part 1

Monday, April 07, 2008 2:41 PM by bart

Dear readers,

I seem to have forgotten the PropertyAttribute definition. Since it's not a piece of rocket science, it should be straightforward to cook your own (use the code snippet in VS).

Cheers,

-Bart

# Adventures in F# - F# 101 Part 8 (Mutables and Reference Cells)

Tuesday, April 15, 2008 4:08 PM by Matthew Podwysocki's Blog

Time for another adventure in F#, covering some of the basics of functional programming and F# in particular

# C# by hobbestobias - Pearltrees

Tuesday, December 27, 2011 8:07 AM by C# by hobbestobias - Pearltrees

Pingback from  C# by hobbestobias - Pearltrees

# C# by markmmullin - Pearltrees

Saturday, January 07, 2012 10:30 AM by C# by markmmullin - Pearltrees

Pingback from  C# by markmmullin - Pearltrees

# Matchtr jor | Askgeorgann

Thursday, March 01, 2012 8:51 AM by Matchtr jor | Askgeorgann

Pingback from  Matchtr jor | Askgeorgann