Last active
July 25, 2020 04:02
-
-
Save ToJans/e57e2170e050a671e07b 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 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 System; | |
namespace FPExplained | |
{ | |
// This is a C# implementation showing what functors, applicatives and monads look like. | |
// | |
// ----><8----------------------------------- | |
// UPDATE: | |
// *********************************** | |
// For a maybe monad, all these constructs look a bit redundant; their reason of existance is more obvious | |
// when you use a list. | |
// | |
// I suggest you to look at the code of the list, before you look at the `maybe` code. | |
// You can find it here: http://tojans.me/blog/2014/06/19/functors-applicatives-and-monads-for-csharp-developers-part-2/ | |
// | |
// After implementing the list; I vastly simplified the code for the maybe class and updated this gist; | |
// now LiftM and LiftA use the same code for the `Maybe` as they do for `FList`. | |
// ----><8----------------------------------- | |
// Begin old comment | |
// ********************************* | |
// | |
// I would have loved to make it generic example (i.e. using interfaces), but this would complicate | |
// it too much, so I opted for an example functor/applicative/monad, i.e. the `Maybe` class. | |
// This is due to the limits of the C# type system, which doesn't allow generic types that take another | |
// generic type as a parameter, f.e. I would need this construct: | |
// | |
// public interface FP<TContext<TObject>> { | |
// // support for functors | |
// public TContext<TNewObject> FMap<TNewObject>(Func<TObject, TNewObject> functor); | |
// // support for applicatives | |
// public TContext<TNewObject> LiftA<TNewObject>(TContext<Func<TObject, TNewObject>> applicative); | |
// // support for monads | |
// public TContext<TNewObject> LiftM<TNewObject>(Func<TObject, TContext<TNewObject>> monad); | |
// } | |
// | |
// I think it would be an interesting exercise to implement other classes, like `List` or `Either`. | |
// | |
// For questions, just contact me (@tojans on twitter). | |
// | |
public class Maybe<TObject> | |
{ | |
private TObject obj = default(TObject); | |
private bool isNothing = true; | |
private Maybe() { } | |
private Maybe(TObject obj) { this.obj = obj; this.isNothing = false; } | |
// A nothing | |
public static Maybe<TObject> Zero() | |
{ | |
return new Maybe<TObject>(); | |
} | |
// A value | |
public static Maybe<TObject> Bind(TObject obj) | |
{ | |
return new Maybe<TObject>(obj); | |
} | |
// convert a Maybe<Maybe<TValue>> into a Maybe<TValue> | |
public static Maybe<TObject> Join(Maybe<Maybe<TObject>> MaybeMaybe) | |
{ | |
return MaybeMaybe.isNothing ? Zero() : MaybeMaybe.obj; | |
} | |
// functor | |
public Maybe<TNewObject> FMap<TNewObject>(Func<TObject, TNewObject> functor) | |
{ | |
return isNothing | |
? Maybe<TNewObject>.Zero() | |
: Maybe<TNewObject>.Bind(functor(this.obj)); | |
} | |
// applicative - maybe applies a maybe<f[m]> maybe<x[n]> | |
// returns a Maybe | |
public Maybe<TNewObject> LiftA<TNewObject>(Maybe<Func<TObject, TNewObject>> applicative) | |
{ | |
return Maybe<TNewObject>.Join(applicative.FMap(functor => this.FMap(functor))); | |
} | |
// monad - returns a joined monad of the result | |
public Maybe<TNewObject> LiftM<TNewObject>(Func<TObject, Maybe<TNewObject>> monad) | |
{ | |
return Maybe<TNewObject>.Join(this.FMap(monad)); | |
} | |
public override string ToString() | |
{ | |
return isNothing | |
? "Nothing<" + typeof(TObject).Name + ">" | |
: "Just<" + typeof(TObject).Name + "> " + obj.ToString(); | |
} | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var nothing_int = Maybe<int>.Zero(); | |
var just____100 = Maybe<int>.Bind(100); | |
var just___1000 = Maybe<int>.Bind(1000); | |
Console.WriteLine(nothing_int.ToString()); // Nothing<Int32> | |
Console.WriteLine(just____100.ToString()); // Just<Int32> 100 | |
Console.WriteLine(just___1000.ToString()); // Just<Int32> 1000 | |
// functor | |
Func<int, int> functor_pow2 = x => x * x; | |
Console.WriteLine(nothing_int.FMap(functor_pow2).ToString()); // Nothing<Int32> | |
Console.WriteLine(just____100.FMap(functor_pow2).ToString()); // Just<Int32> 10000 | |
Console.WriteLine(just___1000.FMap(functor_pow2).ToString()); // Just<Int32> 1000000 | |
var applicative_description = Maybe<Func<int, string>>.Bind(i => "This is the value " + i.ToString()); | |
var applicative_____nothing = Maybe<Func<int, string>>.Zero(); | |
// applicative | |
Console.WriteLine(nothing_int.LiftA(applicative_description).ToString()); // Nothing<String> | |
Console.WriteLine(just____100.LiftA(applicative_description).ToString()); // Just<String> This is the value 100 | |
Console.WriteLine(just___1000.LiftA(applicative_description).ToString()); // Just<String> This is the value 1000 | |
Console.WriteLine(nothing_int.LiftA(applicative_____nothing).ToString()); // Nothing<String> | |
Console.WriteLine(just____100.LiftA(applicative_____nothing).ToString()); // Nothing<String> | |
Console.WriteLine(just___1000.LiftA(applicative_____nothing).ToString()); // Nothing<String> | |
// monad | |
Func<int, Maybe<string>> monad_describe_large_numbers = | |
i => (i > 100) | |
? Maybe<string>.Bind(i.ToString() + " is a large number") | |
: Maybe<string>.Zero(); | |
Console.WriteLine(nothing_int.LiftM(monad_describe_large_numbers).ToString()); // Nothing<String> | |
Console.WriteLine(just____100.LiftM(monad_describe_large_numbers).ToString()); // Nothing<String> | |
Console.WriteLine(just___1000.LiftM(monad_describe_large_numbers).ToString()); // Just<String> 1000 is a large number | |
// combine them! | |
Maybe<Func<string, int>> applicative_strlen = Maybe<Func<string, int>>.Bind(s => s.Length); | |
Console.WriteLine(nothing_int.FMap(functor_pow2).LiftM(monad_describe_large_numbers).LiftA(applicative_strlen).ToString()); | |
// Nothing<Int32> | |
Console.WriteLine(just____100.FMap(functor_pow2).LiftM(monad_describe_large_numbers).LiftA(applicative_strlen).ToString()); | |
// Just<Int32> 23 | |
Console.WriteLine(just___1000.FMap(functor_pow2).LiftM(monad_describe_large_numbers).LiftA(applicative_strlen).ToString()); | |
// Just<Int32> 25 | |
applicative_strlen = Maybe<Func<string, int>>.Zero(); | |
Console.WriteLine(just___1000.FMap(functor_pow2).LiftM(monad_describe_large_numbers).LiftA(applicative_strlen).ToString()); | |
// Nothing<Int32> | |
Console.ReadLine(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment