Last active
April 10, 2025 05:18
-
-
Save afifmohammed/3f4f458435c0866d5106fb4d2563db19 to your computer and use it in GitHub Desktop.
Interpreting IO vai the yield operator
This file contains hidden or 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.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