Last active
April 19, 2017 21:55
-
-
Save iSynaptic/610b0879dd4d39bfb16142593f45150a to your computer and use it in GitHub Desktop.
Task<T> Monad Example
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
void Main() | |
{ | |
Task<int> monadicTask = from x in Task.Factory.StartNew(() => 6) | |
from y in Task.Factory.StartNew(() => 7) | |
let computation = x * y | |
select computation; | |
Console.WriteLine(monadicTask.Result); // writes 42; | |
} | |
public static class TaskExtensions | |
{ | |
private static bool HasTaskCompletedSuccessfully(this Task @this) | |
{ | |
if(@this == null) throw new ArgumentNullException("this"); | |
if(!@this.IsCompleted) throw new InvalidOperationException("You can only test completed tasks."); | |
return !@this.IsFaulted && !@this.IsCanceled; | |
} | |
private static void PropigateTaskOutcome<T>(Task<T> source, TaskCompletionSource<T> tcs) | |
{ | |
if(source == null) throw new ArgumentNullException("source"); | |
if(!source.IsCompleted) throw new InvalidOperationException("You can only propigate completed tasks."); | |
if(tcs == null) throw new ArgumentNullException("tcs"); | |
if(!TryPropigateTaskWithoutResult(source, tcs)) | |
tcs.SetResult (source.Result); | |
} | |
private static bool TryPropigateTaskWithoutResult<T, TTarget>(Task<T> source, TaskCompletionSource<TTarget> tcs) | |
{ | |
if(source == null) throw new ArgumentNullException("source"); | |
if(!source.IsCompleted) throw new InvalidOperationException("You can only propigate completed tasks."); | |
if(tcs == null) throw new ArgumentNullException("tcs"); | |
if(source.IsFaulted) | |
{ | |
tcs.SetException (source.Exception); | |
return true; | |
} | |
if(source.IsCanceled) | |
{ | |
tcs.SetCanceled(); | |
return true; | |
} | |
return false; | |
} | |
private static Task<T> ToTask<T>(this T value) | |
{ | |
return Task.FromResult(value); | |
} | |
// C# compiler optimized select many (implemented in a non-optimal way [ie. using the "real" bind function]) | |
public static Task<TResult> SelectMany<T, TIntermediate, TResult>(this Task<T> @this, Func<T, Task<TIntermediate>> selector, Func<T, TIntermediate, TResult> combiner) | |
{ | |
return @this.SelectTask(t => selector(t).SelectTask(u => combiner(t, u).ToTask())); | |
} | |
//"real" monadic bind function | |
public static Task<TResult> SelectMany<T, TResult>(this Task<T> @this, Func<T, Task<TResult>> selector) | |
{ | |
return SelectTask(@this, selector); | |
} | |
public static Task<TResult> SelectTask<T, TResult>(this Task<T> @this, Func<T, Task<TResult>> selector) | |
{ | |
if(@this == null) throw new ArgumentNullException("this"); | |
if(selector == null) throw new ArgumentNullException("selector"); | |
var tcs = new TaskCompletionSource<TResult>(); | |
@this.ContinueWith (t => | |
{ | |
if(!TryPropigateTaskWithoutResult(t, tcs)) | |
selector(t.Result).ContinueWith (t2 => PropigateTaskOutcome(t2, tcs)); | |
}); | |
return tcs.Task; | |
} | |
public static Task<TResult> Select<T, TResult>(this Task<T> @this, Func<T, TResult> selector) | |
{ | |
if(@this == null) throw new ArgumentNullException("this"); | |
if(selector == null) throw new ArgumentNullException("selector"); | |
var tcs = new TaskCompletionSource<TResult>(); | |
@this.ContinueWith (t => | |
{ | |
if(!TryPropigateTaskWithoutResult(t, tcs)) | |
tcs.SetResult (selector(t.Result)); | |
}); | |
return tcs.Task; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment