Skip to content

Instantly share code, notes, and snippets.

@afifmohammed
Forked from vmarquez/ReaderMonad.cs
Last active January 24, 2017 21:50
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 afifmohammed/2122338565d0420abf4b905f183e9952 to your computer and use it in GitHub Desktop.
Save afifmohammed/2122338565d0420abf4b905f183e9952 to your computer and use it in GitHub Desktop.
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
public static class ReaderMonadExt
{
public static Func<T, C> SelectMany<T, A, B, C>(this Func<T, A> t_a, Func<A, Func<T, B>> a_t_b, Func<A, B, C> ab_c)
{
return t =>
{
var a = t_a(t);
return ab_c(a, a_t_b(a)(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(config);
Console.WriteLine(ret);
}
//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 => Console.WriteLine("!" + str)),
};
}
public static Func<Config, Int32> GetIntFromDB()
{
return 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 Func<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 Func<Config, Guid> GetGuidWithAuth()
{
return config =>
{
config.LogMethod("Getting a GUID");
return new Guid();
};
}
public static Func<Config, Boolean> WriteStuffToDisk(int i, string str)
{
return 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;
};
}
}
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