Skip to content

Instantly share code, notes, and snippets.

@stijnmoreels
Last active February 14, 2018 12:20
Show Gist options
  • Save stijnmoreels/c593a21b9902c5bc6f2185ab478c5a81 to your computer and use it in GitHub Desktop.
Save stijnmoreels/c593a21b9902c5bc6f2185ab478c5a81 to your computer and use it in GitHub Desktop.
Task Extensions with Monad Laws
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());
}
}
}
@samneirinck
Copy link

Nice!

I'd do 1 small modification on line 36. Instead of starting a new thread via Task.Factory.StartNew, I'd use Task.FromResult(value). Unless you really -want- a new thread for this?

@stijnmoreels
Copy link
Author

stijnmoreels commented Feb 13, 2018

Yeah, that would be better; Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment