Skip to content

Instantly share code, notes, and snippets.

@isobit
Last active August 29, 2015 14:26
Show Gist options
  • Save isobit/cb74f987442ac0a3e9eb to your computer and use it in GitHub Desktop.
Save isobit/cb74f987442ac0a3e9eb to your computer and use it in GitHub Desktop.
C# Pattern Matching
using System.Linq;
namespace Util
{
public static class PatternMatcherConversions
{
public static PatternMatcher<T, TRes> Match<T, TRes>(this T o)
{
return new PatternMatcher<T, TRes>(o);
}
public static OrMatchable Or(this object o, object other)
{
return new OrMatchable(new List<IMatchable>
{
new EqualsMatchable(o),
new EqualsMatchable(other)
});
}
public static EqualsMatchable ToMatchable(this object o)
{
return new EqualsMatchable(o);
}
}
public interface IMatchable
{
bool IsMatch(object value);
}
public class EqualsMatchable : IMatchable
{
private object _o;
public EqualsMatchable(object o)
{
_o = o;
}
public bool IsMatch(object value)
{
return _o.Equals(value);
}
public OrMatchable Or(IMatchable left)
{
return new OrMatchable(new List<IMatchable> { this, left });
}
}
public class OrMatchable : IMatchable
{
protected readonly List<IMatchable> Matchables;
public OrMatchable(List<IMatchable> matchables)
{
Matchables = matchables;
}
public bool IsMatch(object value)
{
return Matchables.Any(m => m.IsMatch(value));
}
public OrMatchable Or(IMatchable left)
{
return new OrMatchable(Matchables.Concat(new List<IMatchable> { left } ).ToList());
}
}
public class MatchNotFoundException : Exception
{
public MatchNotFoundException(string msg) : base(msg) { }
}
public class PatternMatcher<T, TResult>
{
private readonly T _value;
private readonly List<Tuple<Predicate<T>, Func<T, TResult>>> _cases;
private Func<T, TResult> _elseFunc;
public PatternMatcher(T value)
{
_value = value;
_cases = new List<Tuple<Predicate<T>, Func<T, TResult>>>();
}
public PatternMatcher<T, TResult> Case(Predicate<T> cond, Func<T, TResult> res)
{
_cases.Add(Tuple.Create(cond, res));
return this;
}
public PatternMatcher<T, TResult> Case<TCase>(Func<TCase, TResult> res) where TCase : class
{
_cases.Add(Tuple.Create<Predicate<T>, Func<T, TResult>>(
v => v is TCase,
v => res(v as TCase)
));
return this;
}
public PatternMatcher<T, TResult> Case(T condLit, Func<T, TResult> res)
{
_cases.Add(Tuple.Create(new Predicate<T>(v => v.Equals(condLit)), res));
return this;
}
public PatternMatcher<T, TResult> Case(IMatchable matchable, Func<T, TResult> res)
{
_cases.Add(Tuple.Create(new Predicate<T>(v => matchable.IsMatch(v)), res));
return this;
}
public PatternMatcher<T, TResult> Else(Func<T, TResult> f)
{
_elseFunc = f;
return this;
}
public TResult Apply()
{
foreach (var item in _cases.Where(item => item.Item1(_value)))
{
return item.Item2(_value);
}
if (_elseFunc != null)
{
return _elseFunc(_value);
}
throw new MatchNotFoundException(string.Format("no match for {0}", _value));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment