-
-
Save afifmohammed/8fff7113ef4e4ce384a971b92c2e4bb5 to your computer and use it in GitHub Desktop.
C# Free Monad
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
// | |
// See https://github.com/louthy/language-ext | |
// | |
using System; | |
using System.IO; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using LanguageExt; | |
using static LanguageExt.Prelude; | |
using static LanguageExt.List; | |
namespace ConsoleApp8 | |
{ | |
public static class TestIO | |
{ | |
static IO<Unit> TotallyPureIOComputation(string path) => | |
from lines in IO.ReadAllLines(path) | |
from _1 in IO.Log($"There are {lines.Count} lines") | |
from _2 in IO.Log($"Prepending line numbers") | |
let newLines = zip(Range(1, Int32.MaxValue), lines) | |
.Map((i, line) => $"{i} {line}") | |
.ToSeq() | |
from _3 in IO.WriteAllLines(path, newLines) | |
from _4 in IO.Log($"Lines prepended and file saved successfully") | |
select unit; | |
public static void Test(string path) | |
{ | |
var computation = TotallyPureIOComputation(path); | |
var mockedResult = MockInterpreter.Interpet(computation); | |
var liveResult = LiveInterpreter.Interpet(computation); | |
var liveAsyncResult = LiveInterpreterAsync.Interpet(computation).Result; | |
} | |
} | |
public static class LiveInterpreter | |
{ | |
public static A Interpet<A>(IO<A> ma) => | |
ma is IO<A>.Return r ? r.Value | |
: ma is IO<A>.Faulted e ? throw new Exception(e.Message) | |
: ma is IO<A>.ReadAllLines ra ? Interpet(ra.Next(File.ReadAllLines(ra.Path).ToSeq())) | |
: ma is IO<A>.WriteAllLines wa ? Interpet(wa.Next(WriteAllLines(wa.Path, wa.Output))) | |
: ma is IO<A>.Log log ? Interpet(log.Next(Log(log.Output))) | |
: throw new NotSupportedException(); | |
static Unit Log(string output) | |
{ | |
Console.WriteLine(output); | |
return unit; | |
} | |
static Unit WriteAllLines(string path, Seq<string> output) | |
{ | |
File.WriteAllLines(path, output.ToArray()); | |
return unit; | |
} | |
} | |
public static class LiveInterpreterAsync | |
{ | |
public static async Task<A> Interpet<A>(IO<A> ma) => | |
ma is IO<A>.Return r ? r.Value | |
: ma is IO<A>.Faulted e ? throw new Exception(e.Message) | |
: ma is IO<A>.ReadAllLines ra ? await Interpet(ra.Next(await ReadAllLines(ra.Path))) | |
: ma is IO<A>.WriteAllLines wa ? await Interpet(wa.Next(await WriteAllLines(wa.Path, wa.Output))) | |
: ma is IO<A>.Log log ? await Interpet(log.Next(Log(log.Output))) | |
: throw new NotSupportedException(); | |
static Unit Log(string output) | |
{ | |
Console.WriteLine(output); | |
return unit; | |
} | |
static Task<Unit> WriteAllLines(string path, Seq<string> output) => Task.Run(() => | |
{ | |
File.WriteAllLines(path, output.ToArray()); | |
return unit; | |
}); | |
static Task<Seq<string>> ReadAllLines(string path) => | |
Task.Run(() => File.ReadAllLines(path).ToSeq()); | |
} | |
public static class MockInterpreter | |
{ | |
public static A Interpet<A>(IO<A> ma) => | |
ma is IO<A>.Return r ? r.Value | |
: ma is IO<A>.Faulted e ? throw new Exception(e.Message) | |
: ma is IO<A>.ReadAllLines ra ? Interpet(ra.Next(MockReadAllLines(ra.Path))) | |
: ma is IO<A>.WriteAllLines wa ? Interpet(wa.Next(WriteAllLines(wa.Path, wa.Output))) | |
: ma is IO<A>.Log log ? Interpet(log.Next(Log(log.Output))) | |
: throw new NotSupportedException(); | |
static Seq<string> MockReadAllLines(string path) => | |
Seq("Hello", "World"); | |
static Unit Log(string output) | |
{ | |
Console.WriteLine(output); | |
return unit; | |
} | |
static Unit WriteAllLines(string path, Seq<string> output) | |
{ | |
return unit; | |
} | |
} | |
public static class IO | |
{ | |
public static IO<A> Return<A>(A value) => new IO<A>.Return(value); | |
public static IO<Unit> Faulted(string error) => new IO<Unit>.Faulted(error, Return); | |
public static IO<Seq<string>> ReadAllLines(string path) => new IO<Seq<string>>.ReadAllLines(path, Return); | |
public static IO<Unit> WriteAllLines(string path, Seq<string> output) => new IO<Unit>.WriteAllLines(path, output, Return); | |
public static IO<Unit> Log(string output) => new IO<Unit>.Log(output, Return); | |
} | |
public static class IOExt | |
{ | |
public static IO<B> Bind<A, B>(this IO<A> ma, Func<A, IO<B>> f) => | |
ma is IO<A>.Return r ? f(r.Value) | |
: ma is IO<A>.Faulted e ? new IO<B>.Faulted(e.Message, n => e.Next(n).Bind(f)) | |
: ma is IO<A>.ReadAllLines ra ? new IO<B>.ReadAllLines(ra.Path, n => ra.Next(n).Bind(f)) | |
: ma is IO<A>.WriteAllLines wa ? new IO<B>.WriteAllLines(wa.Path, wa.Output, n => wa.Next(n).Bind(f)) | |
: ma is IO<A>.Log log ? new IO<B>.Log(log.Output, n => log.Next(n).Bind(f)) as IO<B> | |
: throw new NotSupportedException(); | |
public static IO<B> Select<A, B>(this IO<A> ma, Func<A, B> f) => | |
ma.Bind(a => IO.Return(f(a))); | |
public static IO<C> SelectMany<A, B, C>(this IO<A> ma, Func<A, IO<B>> bind, Func<A, B, C> project) => | |
ma.Bind(a => bind(a).Select(b => project(a, b))); | |
} | |
public abstract class IO<A> | |
{ | |
public class Return : IO<A> | |
{ | |
public readonly A Value; | |
public Return(A value) => | |
Value = value; | |
} | |
public class Faulted : IO<A> | |
{ | |
public readonly string Message; | |
public readonly Func<Unit, IO<A>> Next; | |
public Faulted(string message, Func<Unit, IO<A>> next) | |
{ | |
Message = message; | |
Next = next; | |
} | |
} | |
public class ReadAllLines : IO<A> | |
{ | |
public readonly string Path; | |
public readonly Func<Seq<string>, IO<A>> Next; | |
public ReadAllLines(string path, Func<Seq<string>, IO<A>> next) | |
{ | |
Path = path; | |
Next = next; | |
} | |
} | |
public class WriteAllLines : IO<A> | |
{ | |
public readonly string Path; | |
public readonly Seq<string> Output; | |
public readonly Func<Unit, IO<A>> Next; | |
public WriteAllLines(string path, Seq<string> output, Func<Unit, IO<A>> next) | |
{ | |
Path = path; | |
Output = output; | |
Next = next; | |
} | |
} | |
public class Log : IO<A> | |
{ | |
public readonly string Output; | |
public readonly Func<Unit, IO<A>> Next; | |
public Log(string output, Func<Unit, IO<A>> next) | |
{ | |
Output = output; | |
Next = next; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment