Skip to content

Instantly share code, notes, and snippets.

@dtchepak
Created December 12, 2012 11:30
Show Gist options
  • Save dtchepak/4267103 to your computer and use it in GitHub Desktop.
Save dtchepak/4267103 to your computer and use it in GitHub Desktop.
Monad implementation for Lazy<T>
// See also: http://davesquared.net/2012/12/lazy-monad-csharp.html
public static class LazyExtensions
{
public static Lazy<TResult> Select<T, TResult>(this Lazy<T> value, Func<T, TResult> selector)
{
return new Lazy<TResult>(() => selector(value.Value));
}
public static Lazy<TResult> SelectMany<T, TResult>(this Lazy<T> value, Func<T, Lazy<TResult>> selector)
{
return SelectMany(value, selector, (a, b) => b);
}
public static Lazy<TResult> SelectMany<T, TA, TResult>(this Lazy<T> value, Func<T, Lazy<TA>> selector, Func<T, TA, TResult> resultSelector)
{
return new Lazy<TResult>(() =>
{
var first = value.Value;
var second = selector(first).Value;
return resultSelector(first, second);
});
}
}
public class LazyMonadTests
{
[Test]
public void Map()
{
var lazyInt = new Lazy<int>(() => 42);
var newLazy = lazyInt.Select(x => (x * 2).ToString());
Assert.AreEqual("84", newLazy.Value);
}
[Test]
public void MapDefersExecution()
{
var firstCounter = 0;
var secondCounter = 0;
var first = new Lazy<int>(() => { firstCounter++; return 10; });
var second = first.Select(x => { secondCounter++; return x.ToString(); });
Assert.AreEqual("10", second.Value);
Assert.AreEqual("10", second.Value, "subsequent calls keep same value");
Assert.AreEqual(1, firstCounter, "init first lazy once");
Assert.AreEqual(1, secondCounter, "init second lazy once");
}
[Test]
public void SelectMany()
{
var first = new Lazy<int>(() => 42);
var second = new Lazy<string>(() => "nyan");
var third = from x in first
from str in second
select str + " cat " + x;
Assert.AreEqual("nyan cat 42", third.Value);
}
[Test]
public void SelectManyDefersExecution()
{
var firstCounter = 0;
var secondCounter = 0;
var thirdCounter = 0;
var first = new Lazy<int>(() => { firstCounter++; return 10; });
var second = new Lazy<string>(() => { secondCounter++; return "nyan"; });
var third = from x in first
from str in second
let temp = thirdCounter++
select str + " cat " + x;
Assert.AreEqual("nyan cat 10", third.Value);
Assert.AreEqual(1, firstCounter);
Assert.AreEqual(1, secondCounter);
Assert.AreEqual(1, thirdCounter);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment