Skip to content

Instantly share code, notes, and snippets.

@ToJans
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ToJans/9f13a029800df9ce5709 to your computer and use it in GitHub Desktop.
Save ToJans/9f13a029800df9ce5709 to your computer and use it in GitHub Desktop.
This is a C# implementation showing what functors, applicatives and monads look like.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
namespace FPExplainedV2
{
// another implementation to test category theory; this time I implement a list...
public class FList<TObject>
{
IEnumerable<TObject> items = new TObject[] { };
private FList() { }
public static FList<TObject> Bind(params TObject[] pars)
{
return new FList<TObject>() { items = pars };
}
public static FList<TObject> Bind(IEnumerable<TObject> pars)
{
return FList<TObject>.Bind(pars.ToArray());
}
public static FList<TObject> Zero()
{
return FList<TObject>.Bind();
}
// convert an FList<FList<X>> into FList<X>
public static FList<TObject> Join(FList<FList<TObject>> obj)
{
return FList<TObject>.Bind(obj.items.SelectMany(x => x.items));
}
// functor
public FList<TOtherObject> FMap<TOtherObject>(Func<TObject, TOtherObject> functor)
{
return FList<TOtherObject>.Bind(this.items.Select(functor));
}
// applicative - applies every f[m] to every x[n]
// returns an FList of n * m elements
public FList<TOtherObject> LiftA<TOtherObject>(FList<Func<TObject, TOtherObject>> applicative)
{
// note we use the fmap and join primitives here
return FList<TOtherObject>.Join(applicative.FMap(functor => this.FMap(functor)));
}
// monad - returns a joined FList of all the results
public FList<TOtherObject> LiftM<TOtherObject>(Func<TObject, FList<TOtherObject>> monad)
{
// note we use the fmap and join primitives here
return FList<TOtherObject>.Join(this.FMap(monad));
}
public override bool Equals(object obj)
{
var other = obj as FList<TObject>;
if (other == null) return false;
return other.items.SequenceEqual(this.items);
}
public override int GetHashCode()
{
return this.items.Aggregate(0, (acc, x) => acc ^ x.GetHashCode());
}
public override string ToString()
{
var itemsToPrint = items.Count() < 6
? items.Select(x => x.ToString())
: items.Select(x => x.ToString()).Take(3).Concat(new string[] { "...", items.Last().ToString() });
return "[ " + string.Join(", ", itemsToPrint) + " ]";
}
}
[TestClass]
public class Verify_my_FList_works
{
[TestMethod]
public void Run_some_tests_with_names_covering_all_cases()
{
FList<string> firstnames = FList<string>.Bind("Tom", "Lies", "Quinten", "Matisse");
FList<string> lastnames = FList<string>.Bind("Janssens", "Kint");
Func<string, string> functor_get_name_initial = x => x[0] + ".";
// crap because we don't have currying in CSharp
Func<string, Func<string, string>> functor_convert_a_string_to_a_string_appender_function = lastname =>
{
Func<string, string> ret = (firstname) => firstname + " " + lastname;
return ret;
};
var applicative_lastname_appenders =
lastnames.FMap(functor_convert_a_string_to_a_string_appender_function);
Func<string, FList<string>> monad_duplicate_strings_that_contain_the_letter_Q =
x => x.ToLower().Contains('q')
? FList<string>.Bind(x, x + " [2]")
: FList<string>.Bind(x);
Func<string, FList<string>> monad_remove_strings_that_contain_the_letter_O =
x => x.ToLower().Contains('o')
? FList<string>.Zero()
: FList<string>.Bind(x);
// Functional composition FTW!
var nameCombos = firstnames
.LiftM(monad_remove_strings_that_contain_the_letter_O)
.FMap(functor_get_name_initial)
.LiftA(applicative_lastname_appenders)
.LiftM(monad_duplicate_strings_that_contain_the_letter_Q);
var expected = FList<string>.Bind(
"L. Janssens"
, "Q. Janssens"
, "Q. Janssens [2]"
, "M. Janssens"
, "L. Kint"
, "Q. Kint"
, "Q. Kint [2]"
, "M. Kint"
);
Assert.AreEqual(nameCombos, expected );
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment