I have started putting together a functional programming library in C# which is somewhat inspired by scalaz and the book Functional Programming in Scala.
I am hoping to get some feedback on the Monad Transformers implementations and comments.
Please understand this is implemented in C# and .NET, which does not support typeclasses and Higher Kindedness.
Here is an example demo of what I have been able to create so far with what has been developed.
using NUnit.Framework; using System; using System.Collections.Generic; using System.Threading; using System.Linq; namespace Sharper.Tests { internal class Student { public int Id{ get; set; } public string Name{ get; set; } } internal class Score { public int StudentId{ get; set; } public string Name{ get; set; } public Decimal Result{ get; set; } } internal class ScoreServer { public ScoreServer() { students = new Dictionary<int, Student>(); scores = new Dictionary<int, List<Score>>(); students.Add(1, new Student{ Id = 1, Name = "Blair" }); students.Add(2, new Student{ Id = 2, Name = "Esther" }); scores.Add(1, new List<Score> { new Score{ StudentId = 1, Name = "Maths", Result = 76 }, new Score{ StudentId = 1, Name = "English", Result = 56 }, new Score{ StudentId = 1, Name = "Science", Result = 67 } }); scores.Add(2, new List<Score> { new Score{ StudentId = 2, Name = "Maths", Result = 87 }, new Score{ StudentId = 2, Name = "English", Result = 72 }, new Score{ StudentId = 2, Name = "Science", Result = 59 } }); } public IO<String> GetStudentScoreFor(int studentId, string classname) { var studentT = GetStudentById(studentId).OptionT(); var studentNameT = studentT.Map(x => x.Name) .OrElse(new Some<string>(studentId.ToString())); var matchedScoreT = from student in studentT from score in GetClassScoreByStudent(student.ToOption(), classname).OptionT() select String.Format("Student: {0} Recieved {1} for {2}", student.Name, score.Name, score.Result); var result = from name in studentNameT from text in matchedScoreT.Map(score => score) .OrElse(String.Format("Student {0} has not record for {1}", name, classname) .ToOption()) select text; return result.GetValueOrDefault(String.Empty); } public IO<Option<Student>> GetStudentById(int studentId) { return new IO<Option<Student>>( () => { Thread.Sleep(TimeSpan.FromSeconds(1.5)); // Simulate work return students.SafeGet(studentId); } ); } public IO<Option<Score>> GetClassScoreByStudent(Option<Student> student, string @class) { return new IO<Option<Score>>( () => { Thread.Sleep(TimeSpan.FromSeconds(1)); // Simulate work return student.FlatMap(x => scores.SafeGet(x.Id) .FlatMap(y => y.FirstOrDefault(z => z.Name == @class) .ToOption())); } ); } private Dictionary<int,Student> students{ get; set; } private Dictionary<int, List<Score>> scores { get; set; } } [TestFixture] public class OptionIODemoTests { [Test] public void Testing_ScoreServer1() { var server = new ScoreServer(); var result = server.GetStudentScoreFor(1, "Maths").PerformUnsafeIO(); Assert.AreEqual(String.Format("Student: {0} Recieved {1} for {2}", "Blair", "Maths", 76), result); } [Test] public void Testing_ScoreServer2() { var server = new ScoreServer(); var result = server.GetStudentScoreFor(1, "Mathematics").PerformUnsafeIO(); Assert.AreEqual(String.Format("Student {0} has not record for {1}", "Blair", "Mathematics"), result); } [Test] public void Testing_ScoreServer3() { var server = new ScoreServer(); var result = server.GetStudentScoreFor(3, "Mathematics").PerformUnsafeIO(); Assert.AreEqual(String.Format("Student {0} has not record for {1}", "3", "Mathematics"), result); } } }
The main projects site is here.