Last active
August 29, 2015 14:02
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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