Skip to content

Instantly share code, notes, and snippets.

@bgrainger
Last active December 15, 2015 15:09
Show Gist options
  • Save bgrainger/5279623 to your computer and use it in GitHub Desktop.
Save bgrainger/5279623 to your computer and use it in GitHub Desktop.
Tasks as monads.
class Program
{
public static void Main()
{
string urls = @"http://www.google.com/
http://www.microsoft.com/
http://www.github.com/
http://news.ycombinator.com/
http://stackoverflow.com/
http://www.logos.com/";
using (TextReader reader = new StringReader(urls))
{
List<Task<string>> tasks = new List<Task<string>>();
for (int i = 0; i < 2; i++)
{
// chain SelectMany calls together
var median1 =
from uri in reader.ReadLineAsync()
from data in new WebClient().DownloadStringTaskAsync(new Uri(uri))
from lines in Task.FromResult(SplitIntoLines(data))
from sorted in Task.Factory.StartNew(() => { Array.Sort(lines); return lines; })
select sorted[sorted.Length / 2];
tasks.Add(median1);
// SelectMany followed by Select
var median2 =
from uri in reader.ReadLineAsync()
from data in new WebClient().DownloadStringTaskAsync(new Uri(uri))
let lines = SplitIntoLines(data)
let sorted = lines.OrderBy(x => x).ToArray()
select sorted[sorted.Length / 2];
tasks.Add(median2);
// mix of SelectMany and Select (NOTE: Dispose is not called)
var median3 =
from uri in reader.ReadLineAsync()
let req = WebRequest.Create(uri)
from resp in req.GetResponseAsync()
let stream = resp.GetResponseStream()
let streamReader = new StreamReader(stream)
from data in streamReader.ReadToEndAsync()
let lines = SplitIntoLines(data)
let sorted = lines.OrderBy(x => x).ToArray()
select sorted[sorted.Length / 2];
tasks.Add(median3);
}
Task.WaitAll(tasks.ToArray());
tasks.ForEach(t => Console.WriteLine(t.Result));
}
}
public static string[] SplitIntoLines(string value)
{
return value.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
}
}
public static class TaskExtensions
{
public static Task<TResult> Select<TInput, TResult>(this Task<TInput> task, Func<TInput, TResult> selector)
{
return task.ContinueWith(t => selector(t.Result));
}
#if !CSHARP50
public static Task<TResult> SelectMany<TInput, TIntermediate, TResult>(this Task<TInput> task, Func<TInput, Task<TIntermediate>> selector, Func<TInput, TIntermediate, TResult> resultSelector)
{
return task.ContinueWith(t => selector(t.Result))
.Unwrap()
.ContinueWith(t => resultSelector(task.Result, t.Result));
}
#else
public static async Task<TResult> SelectMany<TInput, TIntermediate, TResult>(this Task<TInput> task, Func<TInput, Task<TIntermediate>> selector, Func<TInput, TIntermediate, TResult> resultSelector)
{
TInput input = await task;
TIntermediate intermediate = await selector(input);
return resultSelector(input, intermediate);
}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment