Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

This is an example of using the Reader Monad for Dependency Injection using LINQ in C#. I learned about this from Tony Morris and Rúnar Bjarnason's video here: http://www.youtube.com/watch?v=ECPGTUa1WAI To figure out the (slightly weird) SelectMany peculiarities I found http://mikehadlow.blogspot.com/2011/01/monads-in-c-4-linq-loves-monads.html

View ReaderMonad.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
//Reader Monad and its extension class to give it SelectMany(bind/flatMap) capabilities for use in LINQ queries
public static class ReaderMonadExt
{
public static ReaderMonad<T, C> SelectMany<T, A, B, C>(this ReaderMonad<T, A> rm, Func<A, ReaderMonad<T, B>> bindf, Func<A, B, C> select)
{
return new ReaderMonad<T, C>(t =>
{
var a = rm.Run(t);
return select(a, bindf(a).Run(t));
});
}
}
 
public class ReaderMonad<T, R>
{
private Func<T, R> _run;
 
public ReaderMonad(Func<T, R> run)
{
_run = run;
}
 
public R Run(T t)
{
return _run(t);
}
}
 
 
class ReaderMonadUsageExample
{
public static void Main(String[] args)
{
var config = GetConfig();
//Notice how LINQ gives us nice syntax opposed to nested calls to SelectMany.
var readerMonad =
from intDb in GetIntFromDB()
from netstr in GetStrFromNetwork()
from writeSuccess in WriteStuffToDisk(intDb, netstr)
select (writeSuccess ? "We wrote " + intDb + " and " + netstr + " to disk" : "error!");
 
var ret = readerMonad.Run(config);
Console.WriteLine(ret);
}
 
public static ReaderMonad<Config, Int32> GetIntFromDB()
{
return new ReaderMonad<Config, Int32>(config =>
{
config.LogMethod("Getting an int from the DB using the credentials" + config.AuthInfo);
//some db code logging in with authinfo
return 4;
});
}
 
//this method shows nested composition through the magic of SelectMany
public static ReaderMonad<Config, String> GetStrFromNetwork()
{
return (from aDbInt in GetIntFromDB()
from aGuid in GetGuidWithAuth()
//another method call returning a ReaderMonad that would go out over the network and get some data
select (aDbInt + "|" + aGuid + "|"));
}
 
 
private static ReaderMonad<Config, Guid> GetGuidWithAuth()
{
return new ReaderMonad<Config,Guid>(config =>
{
config.LogMethod("Getting a GUID");
return new Guid();
});
}
public static ReaderMonad<Config, Boolean> WriteStuffToDisk(int i, string str)
{
return new ReaderMonad<Config, Boolean>(config =>
{
config.LogMethod("attempting to write this to disk =" + i + str + " with the credentials " + config.AuthInfo);
//write to disk if all goes well
return true;
});
}
 
public static void MyCustomLog(String str)
{
Console.WriteLine("!" + str);
}
//What we are trying to Inject into all our methods. In this example we have authorization and a logging method
//Likely you could use this for sql connections, transactions, auth credentials, a pool of resources, etc.
public static Config GetConfig()
{
return new Config()
{
AuthInfo = "vmarquez",
LogMethod = (str => MyCustomLog(str)),
};
}
}
 
class Config
{
public String AuthInfo { get; set; }
public Action<String> LogMethod { get; set; }
}
 
 
 
 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.