Last active
February 14, 2018 12:20
-
-
Save stijnmoreels/c593a21b9902c5bc6f2185ab478c5a81 to your computer and use it in GitHub Desktop.
Task Extensions with Monad Laws
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.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace System.Threading.Tasks | |
{ | |
public class Workspace | |
{ | |
/// <summary> | |
/// Initializes a new instance of the <see cref="Workspace"/> class. | |
/// </summary> | |
public Workspace() | |
{ | |
0.Return() | |
.Select(i => i + 10) | |
.Where(i => i % 2 == 0) | |
.SelectMany(i => i.Return()) | |
.Join(2.Return(), a => a, b => b, (a, b) => a + b) | |
.Zip(4.Return(), (a, b) => a + b) | |
.Apply(new Func<int, int>(i => i + 5).Return()) | |
.Do(i => Console.WriteLine("Result: " + i)); | |
} | |
} | |
public static class TaskExtensions | |
{ | |
public static Task<TA> Return<TA>(this Task task, TA value) | |
{ | |
return task.ContinueWith(t => value); | |
} | |
public static Task<TA> Return<TA>(this TA value) | |
{ | |
return Task.FromResult(value); | |
} | |
public static Task<TA> Do<TA>(this Task<TA> source, Action<TA> action) | |
{ | |
return source.SelectMany(a => { action(a); return source; }); | |
} | |
public static Task<TB> Apply<TA, TB>(this Task<TA> source, Task<Func<TA, TB>> taskFunc) | |
{ | |
return source.SelectMany(a => taskFunc.Select(func => func(a))); | |
} | |
public static Task<TB> SelectMany<TA, TB>(this Task<TA> source, Func<TA, Task<TB>> selector) | |
{ | |
return source.ContinueWith(xTask => selector(xTask.Result)).Unwrap(); | |
} | |
public static Task<TB> Select<TA, TB>(this Task<TA> source, Func<TA, TB> selector) | |
{ | |
return source.SelectMany(x => selector(x).Return()); | |
} | |
public static Task<TA> Where<TA>(this Task<TA> source, Func<TA, bool> predicate) | |
{ | |
return source.SelectMany(x => predicate(x) | |
? x.Return() | |
: throw new OperationCanceledException("Value doesn't pass predicate: " + x)); | |
} | |
public static Task<TC> Zip<TA, TB, TC>( | |
this Task<TA> source, | |
Task<TB> other, | |
Func<TA, TB, TC> selector) | |
{ | |
return source.SelectMany(a => other.Select(b => selector(a, b))); | |
} | |
public static Task<TD> Join<TA, TB, TC, TD>( | |
this Task<TA> source, | |
Task<TB> inner, | |
Func<TA, TC> outerKeySelector, | |
Func<TB, TC> innerKeySelector, | |
Func<TA, TB, TD> resultSelector) | |
{ | |
Task.WaitAll(source, inner); | |
return source.SelectMany(ta => inner.SelectMany(tb => | |
{ | |
TC outerC = outerKeySelector(ta); | |
TC innerC = innerKeySelector(tb); | |
return EqualityComparer<TC>.Default.Equals(outerC, innerC) | |
? Task.FromResult(resultSelector(ta, tb)) | |
: throw new OperationCanceledException("Not Equal: " + outerC + " != " + innerC); | |
})); | |
} | |
public static Task<TD> GroupJoin<TA, TB, TC, TD>( | |
this Task<TA> source, | |
Task<TB> inner, | |
Func<TA, TC> outerKeySelector, | |
Func<TB, TC> innerKeySelector, | |
Func<TA, Task<TB>, TD> resultSelector) | |
{ | |
return source.SelectMany(a => | |
resultSelector( | |
a, | |
inner.Where( | |
b => EqualityComparer<TC>.Default.Equals(outerKeySelector(a), | |
innerKeySelector(b)))) | |
.Return()); | |
} | |
} | |
} |
Yeah, that would be better; Thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice!
I'd do 1 small modification on line 36. Instead of starting a new thread via
Task.Factory.StartNew
, I'd useTask.FromResult(value)
. Unless you really -want- a new thread for this?