-
-
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
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
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