Project Description
Simple Functional Programming Library for C#. It introduces several features found in programming languages like F#. It is built in a compositional fashion starting from Pattern Matching and building on that to support Object Expressions, Tuples, Active Patterns, ADTs.
Recommended Reading
Functional Programming for the Real World: With Examples in F# and C#
Pattern Matching
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>();
F# Equivalent
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)
C# with Active Patterns
Func<Expression, string> toString = null;
toString = exp =>
exp.Match()
.Lambda((args, body) => toString(body))
.Param ((name) => name)
.Add ((l, r) => String.Format("({0} + {1})", toString(l), toString(r)))
.Mult ((l, r) => String.Format("{0} * {1}", toString(l), toString(r)))
.Return<string>();
C# Arithmetic Pattern n+ k
Func<int, bool> even = null;
Func<int, bool> odd = null;
even = exp => exp.Match()
.With(n => 0, _ => true)
.With(n => n + 1, n => odd(n))
.Return<bool>();
odd = exp => !even(exp);
Haskell equivalent
even 0 = True
even (n + 1) = odd n
odd n = not (even n)
C# Lists Pattern Matching
Func<Func<char, char>, Func<IEnumerable<char>, IEnumerable<char>>> map = null;
map = f => s => s.Match()
.List((x, xs) => f(x).Cons(map(f)(xs)))
.Any(() => s)
.Return<IEnumerable<char>>();
var toUpper = map(Char.ToUpper);
F# Equivalent
let rec map f xs = match xs with
| x::xs -> f(x)::(map f xs)
| _ -> xs;;
let toUpper = map System.Char.ToUpper
Object Expressions
C#
var foo = ObjectExpression.New<IFoo>()
.With(o => o.A, () => "Hello")
.Return();
F# Equivalent
let foo = { new IFoo with
member x.A () = "Hello" }
Sample IoC Container
var container = ObjectExpression
.New<IoC_Container>()
.With(o => o.Get<IFoo>, () => ObjectExpression.New<IFoo>().Return())
.With(o => o.Get<IBar>, () => ObjectExpression.New<IBar>().Return())
.Return();
var foo = container.Get<IFoo>();
var bar = container.Get<IBar>();
Sorted List (Create an IComparer on the fly ;))
var list = new SortedList<int, string>(
ObjectExpression
.New<IComparer<int>>()
.With(o => o.Compare, (int x, int y) => x - y)
.Return());
C# Discriminated Unions
public interface Exp
{
Exp Var(string x);
Exp Lam(string x, Exp e);
Exp Let(string x, Exp e1, Exp e2);
Exp App(Exp e1, Exp e2);
}
var exp = DataType.New<Exp>();
exp = exp.Let("compose",
exp.Lam("f",
exp.Lam("g",
exp.Lam("x",
exp.App(exp.Var("g"), exp.App(exp.Var("f"), exp.Var("x")))))),
exp.Var("compose"));
Func<Exp, string> toString = null;
toString = expr => expr.Match()
.With(o => o.Var, (string x) => x)
.With(o => o.Let, (string x, Exp e1, Exp e2) => string.Format("let {0} = {1} in {2}", x, toString(e1), toString(e2)))
.With(o => o.Lam, (string x, Exp e) => string.Format("fun {0} -> {1}", x, toString(e)))
.With(o => o.App, (Exp e1, Exp e2) => string.Format("({0} {1})", toString(e1), toString(e2)))
.Return<string>();
Assert.AreEqual("let compose = fun f -> fun g -> fun x -> (g (f x)) in compose", toString(exp));
F# Equivalent
type Exp = Var of string
| Lam of string * Exp
| Let of string * Exp * Exp
| App of Exp * Exp
let composeAst = Let("compose",
Lam("f",
Lam("g",
Lam ("x",
App(Var "g", App(Var "f", Var "x"))))),
Var "compose")
let rec toString exp =
match exp with
| Var x -> x
| Let (x, e1, e2) -> String.Format("let {0} = {1} in {2}", x, toString(e1), toString(e2))
| Lam (x, e) -> String.Format("fun {0} -> {1}", x, toString(e))
| App (e1, e2) -> String.Format("({0} {1})", toString(e1), toString(e2))
let ret = toString composeAst
Monadic Parser
C#
public static IParser<IEnumerable<char>, IEnumerable<string>> String()
{
return CharParser.Word()
.AsString()
.SepBy(CharParser.Whitespace()
.Many1()
.AsString());
}
CollectionAssert.AreEqual
(new[] {"Welcome", "to", "the", "real", "world"},
StringParser.String().ParseString("Welcome to the real world").First());