Created
July 9, 2015 15:27
-
-
Save mikehadlow/c33802cb6d4d1d2fee81 to your computer and use it in GitHub Desktop.
Composing monadic async results using Linq
This file contains 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
using System; | |
using System.Threading.Tasks; | |
namespace Monads | |
{ | |
public static class AwaitableCompositionSpike | |
{ | |
/// <summary> | |
/// First spike composes three aysnc results using Bind(..) extension method | |
/// </summary> | |
public async static void Spike() | |
{ | |
var result = await FirstOperation() // change to FirstOperationFails() to see failure case | |
.Bind(x => SecondOperation(x) | |
.Bind(ThirdOperation)); | |
Console.Out.WriteLine(result); | |
} | |
/// <summary> | |
/// Second spike uses Linq syntax to compose the same async results | |
/// </summary> | |
public async static void SpikeWithLinq() | |
{ | |
var result = await | |
( | |
from a in FirstOperationFails() // change to FirstOperationFails() to see failure case | |
from b in SecondOperation(a) | |
from c in ThirdOperation(b) | |
select c | |
); | |
Console.Out.WriteLine(result); | |
} | |
// some async operations ... | |
public static Task<Result<string>> FirstOperation() | |
{ | |
return Task.FromResult(Result<string>.Success("Hello!")); | |
} | |
public static Task<Result<string>> FirstOperationFails() | |
{ | |
return Task.FromResult(Result<string>.Fail("I failed!")); | |
} | |
public static Task<Result<int>> SecondOperation(string input) | |
{ | |
return Task.FromResult(Result<int>.Success(input.Length)); | |
} | |
public static Task<Result<Thing>> ThirdOperation(int length) | |
{ | |
return Task.FromResult(Result<Thing>.Success(new Thing {Value = length.ToString()})); | |
} | |
/// <summary> | |
/// Monadic Bind | |
/// </summary> | |
public static async Task<Result<B>> Bind<A, B>(this Task<Result<A>> a, Func<A, Task<Result<B>>> func) | |
{ | |
var aResult = await a; | |
if (!aResult.Succeeded) | |
{ | |
return Result<B>.Fail(aResult.Message); | |
} | |
return await func(aResult.Value); | |
} | |
public static Task<Result<T>> ToTaskResult<T>(this T value) | |
{ | |
return Task.FromResult(Result<T>.Success(value)); | |
} | |
/// <summary> | |
/// Linq compatible SelectMany | |
/// </summary> | |
public static Task<Result<C>> SelectMany<A, B, C>( | |
this Task<Result<A>> a, | |
Func<A, Task<Result<B>>> func, | |
Func<A, B, C> select) | |
{ | |
return a.Bind(aval => | |
func(aval).Bind(bval => | |
select(aval, bval).ToTaskResult())); | |
} | |
} | |
public class Thing | |
{ | |
public string Value { get; set; } | |
public override string ToString() | |
{ | |
return Value; | |
} | |
} | |
/// <summary> | |
/// Represents an operation that can succeed or fail | |
/// </summary> | |
public class Result<T> | |
{ | |
public T Value { get; private set; } | |
public bool Succeeded { get; private set; } | |
public string Message { get; private set; } | |
public static Result<T> Success(T value) | |
{ | |
return new Result<T> | |
{ | |
Value = value, | |
Succeeded = true | |
}; | |
} | |
public static Result<T> Fail(string message) | |
{ | |
return new Result<T> | |
{ | |
Message = message, | |
Succeeded = false | |
}; | |
} | |
public override string ToString() | |
{ | |
if (Succeeded) return Value.ToString(); | |
return Message; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Check out:
http://blogs.msdn.com/b/pfxteam/archive/2010/04/04/9990343.aspx
https://code.msdn.microsoft.com/Samples-for-Parallel-b4b76364/sourcecode?fileId=44488&pathId=1818445489