Skip to content

Instantly share code, notes, and snippets.

@afifmohammed
Last active October 9, 2019 19:14
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save afifmohammed/3f4f458435c0866d5106fb4d2563db19 to your computer and use it in GitHub Desktop.
Interpreting IO vai the yield operator
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static async Task Main(string[] args)
{
var instructions = Compose();
await new ConsoleIOInterpreter(
ConsoleProgram.ReadAllLines,
ConsoleProgram.Log,
ConsoleProgram.WriteAllLines,
ConsoleProgram.ReadFileName).Interpret(instructions);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
public static IEnumerable<IO> Compose()
{
yield return IO(
new ReadFileName(),
out Lazy<string> path);
yield return IO(
new ReadAllLines(path.Value),
out Lazy<IEnumerable<string>> lines);
yield return Log($"There are {lines.Value.Count()} lines");
yield return Log("Prepending line numbers");
var newLines = Enumerable
.Range(1, int.MaxValue)
.Zip(lines.Value, (i, line) => $"{i}: {line}");
var newFile = path.Value + ".prefixed";
yield return IO(new WriteAllLines(newFile, newLines));
yield return Log($"Lines prepended and file saved successfully to \"{newFile}\"");
}
private static IO Log(string message) => IO(new Log(message));
private static IO IO<TInput>(TInput input) => new IO<TInput>(input);
private static IO IO<TInput, TOutput>(TInput input, out Lazy<TOutput> output)
{
var list = new List<TOutput>();
output = new Lazy<TOutput>(list.First);
return new IO<TInput, TOutput>(input, list.Add);
}
}
public readonly struct WriteAllLines
{
public readonly string Path;
public readonly IEnumerable<string> Lines;
public WriteAllLines(string path, IEnumerable<string> lines) => (Path, Lines) = (path, lines);
}
public readonly struct ReadAllLines
{
public readonly string Path;
public ReadAllLines(string path) => Path = path;
}
public readonly struct ReadFileName
{
public string Message => "Please provide the name of a text file that you would like to read from and hit enter";
}
public readonly struct Log
{
public readonly string Message;
public Log(string message) => Message = message;
}
public class ConsoleIOInterpreter
{
private readonly Func<ReadAllLines, Task<IEnumerable<string>>> readAllLines;
private readonly Action<Log> log;
private readonly Func<WriteAllLines, Task> writeAllLines;
private readonly Func<ReadFileName, string> readFileName;
public ConsoleIOInterpreter(
Func<ReadAllLines, Task<IEnumerable<string>>> readAllLines,
Action<Log> log,
Func<WriteAllLines, Task> writeAllLines,
Func<ReadFileName, string> readFileName)
{
this.readAllLines = readAllLines;
this.writeAllLines = writeAllLines;
this.log = log;
this.readFileName = readFileName;
}
public async Task Interpret(IEnumerable<IO> instructions)
{
foreach (var instruction in instructions)
{
switch (instruction)
{
case IO<Log> x:
x.Interpret(log);
break;
case IO<ReadAllLines, IEnumerable<string>> x:
await x.Interpret(readAllLines);
break;
case IO<WriteAllLines> x:
await x.Interpret(writeAllLines);
break;
case IO<ReadFileName, string> x:
x.Interpret(readFileName);
break;
default: throw new NotSupportedException($"Not supported operation {instruction}");
}
}
}
}
class ConsoleProgram
{
public static string ReadFileName(ReadFileName x)
{
Console.WriteLine(x.Message);
return Console.ReadLine();
}
public static async Task WriteAllLines(WriteAllLines x)
{
using (StreamWriter sw = new StreamWriter(x.Path))
{
foreach (var line in x.Lines)
{
await sw.WriteLineAsync(line);
}
}
}
public static void Log(Log x) => Console.WriteLine(x.Message);
public static async Task<IEnumerable<string>> ReadAllLines(ReadAllLines x)
{
var lines = new List<string>();
using (StreamReader sr = new StreamReader(x.Path))
{
while (sr.Peek() >= 0)
{
var r = await sr.ReadLineAsync();
lines.Add(r);
}
}
return lines;
}
}
public struct Unit
{
public static readonly Unit unit = new Unit();
}
public interface IO { }
public class IO<TInput> : IO
{
public IO(TInput input)
{
this.input = input;
}
protected readonly TInput input;
public void Interpret(Action<TInput> interpreter)
{
interpreter(input);
}
public async Task Interpret(Func<TInput, Task> interpreter)
{
await interpreter(input);
}
}
public class IO<TInput, TOutput> : IO
{
public IO(TInput input, Action<TOutput> assignOutput)
{
this.input = input;
this.assignOutput = assignOutput;
}
protected readonly TInput input;
private readonly Action<TOutput> assignOutput;
public void Interpret(Func<TInput, TOutput> interpreter)
{
assignOutput(interpreter(input));
}
public async Task Interpret(Func<TInput, Task<TOutput>> interpreter)
{
var o = await interpreter(input);
assignOutput(o);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment