Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing pattern matching in C#

In Scala, you can use pattern matching to produce a result depending on the type of the input. For instance:

val title = content match {
    case blogPost: BlogPost => blogPost.blog.title + ": " + blogPost.title
    case blog: Blog => blog.title
}

In C#, I'd ideally like to be able to write:

var title = Visit(content,
    (BlogPost blogPost) => blogPost.Blog.Title + ": " + blogPost.Title,
    (Blog blog) => blog.Title
);

Is this possible? When I've tried writing it as a single method, I don't know how to specify the generics. The following implementation seems right, apart from getting the type checker to allow functions that accept subtypes of T:

    public TResult Visit<T, TResult>(T value, params Func<T, TResult>[] visitors)
    {
        foreach (var visitor in visitors)
        {
            if (visitor.Method.GetGenericArguments()[0].IsAssignableFrom(value.GetType()))
            {
                return visitor(value);
            }
        }
        throw new ApplicationException("No match");
    }

The closest I've gotten is to add the functions to an object individually, and then call visit on a value:

    public class Visitor<T, TResult>
    {
        private class Result
        {
            public bool HasResult;
            public TResult ResultValue;
        }

        private readonly IList<Func<T, Result>> m_Visitors = new List<Func<T, Result>>();

        public TResult Visit(T value)
        {
            foreach (var visitor in m_Visitors)
            {
                var result = visitor(value);
                if (result.HasResult)
                {
                    return result.ResultValue;
                }
            }
            throw new ApplicationException("No match");
        }

        public Visitor<T, TResult> Add<TIn>(Func<TIn, TResult> visitor) where TIn : T
        {
            m_Visitors.Add(value =>
            {
                if (value is TIn)
                {
                    return new Result { HasResult = true, ResultValue = visitor((TIn)value) };
                }
                return new Result { HasResult = false };
            });
            return this;
        }
    }

This can be used like so:

var title = new Visitor<IContent, string>()
    .Add((BlogPost blogPost) => blogPost.Blog.Title + ": " + blogPost.Title)
    .Add((Blog blog) => blog.Title)
    .Visit(content);

Any idea how to do this with a single method call?

like image 768
Michael Williamson Avatar asked May 17 '11 13:05

Michael Williamson


People also ask

What is the pattern matching in C?

Pattern matching in C− We have to find if a string is present in another string, as an example, the string "algorithm” is present within the string "naive algorithm". If it is found, then its location (i.e. position it is present at) is displayed.

Can I use regex in C?

A regular expression is a sequence of characters used to match a pattern to a string. The expression can be used for searching text and validating input. Remember, a regular expression is not the property of a particular language. POSIX is a well-known library used for regular expressions in C.

What is pattern matching explain with example?

When pattern matching, we assert that a certain piece of data is equal to a certain pattern. For example, in the function: head (element:list) = element. We assert that the first element of head 's argument is called element, and the function returns this.

What is pattern matching syntax?

Pattern matching is a technique where you test an expression to determine if it has certain characteristics. C# pattern matching provides more concise syntax for testing expressions and taking action when an expression matches.


1 Answers

Pattern matching is one of those lovely features mostly found in functional programming languages like F#. There is a great project going on in codeplex named Functional C#. Consider the following F# code:

let operator x = match x with
                 | ExpressionType.Add -> "+"

let rec toString exp = match exp with
                       | LambdaExpression(args, body) -> toString(body)
                       | ParameterExpression(name) -> name
                       | BinaryExpression(op,l,r) -> sprintf "%s %s %s" (toString l) (operator op) (toString r)

Using the Functional C# library, the C# equivalent would be:

var Op = new Dictionary<ExpressionType, string> { { ExpressionType.Add, "+" } };

Expression<Func<int,int,int>> add = (x,y) => x + y;

Func<Expression, string> toString = null;
 toString = exp =>
 exp.Match()
    .With<LambdaExpression>(l => toString(l.Body))
    .With<ParameterExpression>(p => p.Name)
    .With<BinaryExpression>(b => String.Format("{0} {1} {2}", toString(b.Left), Op[b.NodeType], toString(b.Right)))
    .Return<string>();
like image 177
Alireza Maddah Avatar answered Oct 07 '22 01:10

Alireza Maddah