-
I can't find examples of how the free monad works with HKT in C#. I am trying to learn about algebraic effects. Maybe it would be a good idea to add an example test of algebraic free monads, similar to this one https://github.com/tonivade/purefun/blob/master/free/src/test/java/com/github/tonivade/purefun/free/FreeAlgTest.java? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
They're not written up yet because I haven't done the full release. But the documentation is coming soon as well as a blog article on my blog. The API for the But, here's a quick example... Obviously, the point of free monads is to give you a monad for free from a public abstract record Op<A> : K<Op, A>;
public record ReadLinesOp<A>(string Path, Func<Seq<string>, A> Next): Op<A>;
public record WriteLinesOp<A>(string Path, Seq<string> Lines, Func<Unit, A> Next) : Op<A>; The ADT is a simple union of two operations: Any operations that terminate the computation (like an error case) do not need a Now I'll make the public partial class Op : Functor<Op>
{
public static K<Op, B> Map<A, B>(Func<A, B> f, K<Op, A> ma) =>
ma switch
{
ReadLinesOp<A> (var path, var next) => new ReadLinesOp<B>(path, x => f(next(x))),
WriteLinesOp<A> (var path, var lines, var next) => new WriteLinesOp<B>(path, lines, x => f(next(x)))
};
}
Notice how the With those basics we can now lift the ADT into the public partial class Op
{
public static Free<Op, Seq<string>> readLines(string path) =>
Free.lift(new ReadLinesOp<Seq<string>>(path, identity));
public static Free<Op, Unit> writeLines(string path, Seq<string> lines) =>
Free.lift(new WriteLinesOp<Unit>(path, lines, identity));
}
Now they're usable in LINQ (they're a monad): var repr = from lines in Op.readLines("c:\\temp\\test.txt")
from _ in Op.writeLines("c:\\temp\\test2.txt", lines)
select unit;
static IO<A> Interpret<A>(K<Free<Op>, A> ma) =>
ma switch
{
Pure<Op, A>(var value) =>
IO.Pure(value),
Bind<Op, A>(var bind) =>
bind switch
{
ReadLinesOp<Free<Op, A>> (var path, var next) =>
ReadLines(path).Map(next).Bind(Interpret),
WriteLinesOp<Free<Op, A>> (var path, var lines, var next) =>
WriteLines(path, lines).Map(next).Bind(Interpret)
}
}; There are two cases for a
So, you pattern match on the operations run them for real, map the result, and then recursively call Note, this example interpreter uses the These are the static IO<Seq<string>> ReadLines(string path) =>
IO.liftAsync(() => File.ReadAllLinesAsync(path)).Map(toSeq);
static IO<Unit> WriteLines(string path, Seq<string> lines) =>
IO.liftAsync(async () =>
{
await File.WriteAllLinesAsync(path, lines);
return unit;
}); So, with all that in place you can now run the interpreter with the var repr = from lines in Op.readLines("c:\\temp\\test.txt")
from _ in Op.writeLines("c:\\temp\\test2.txt", lines)
select unit;
var comp = Interpret(repr);
comp.Run();
I hope that helps 👍 |
Beta Was this translation helpful? Give feedback.
They're not written up yet because I haven't done the full release. But the documentation is coming soon as well as a blog article on my blog. The API for the
Free
monad is also quite minimal right now, it may well expand to make it easier to use. However, it's not really high up my list right now.But, here's a quick example...
Obviously, the point of free monads is to give you a monad for free from a
Functor
and so first I'll define an ADT and make a functor for it.The ADT is a si…