Skip to content

Commit 93e3ef2

Browse files
committed
added simple free monad implementation
1 parent cba140c commit 93e3ef2

File tree

2 files changed

+112
-5
lines changed

2 files changed

+112
-5
lines changed

LaYumba.Functional/Free.cs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace LaYumba.Functional
5+
{
6+
using static F;
7+
8+
// free case classes
9+
public abstract class Free<T>
10+
{
11+
public abstract R Match<R>(Func<T, R> Done
12+
, Func<Coyo<object, Free<T>>, R> More);
13+
}
14+
15+
// aka Return
16+
internal class Done<T> : Free<T>
17+
{
18+
public T Value { get; }
19+
20+
public Done(T value) { Value = value; }
21+
22+
public override R Match<R>(Func<T, R> Done
23+
, Func<Coyo<object, Free<T>>, R> More) => Done(Value);
24+
}
25+
26+
// aka Bind, Suspend
27+
internal class More<T> : Free<T>
28+
{
29+
public Coyo<object, Free<T>> Next { get; }
30+
31+
public More(Coyo<object, Free<T>> next) { Next = next; }
32+
33+
public override R Match<R>(Func<T, R> Done
34+
, Func<Coyo<object, Free<T>>, R> More) => More(Next);
35+
}
36+
37+
public static class Free
38+
{
39+
// Lifts a T into a Free<T> that has the given T as its inner value
40+
public static Free<T> Return<T>(T t) => Done(t);
41+
42+
// Runs a Free<T> with the given interpreter, returning a T
43+
public static T Run<T>
44+
(this Free<T> free, Func<object, object> interpret)
45+
=> free.Match(
46+
Done: t => t,
47+
More: coyo => Run(coyo.Func(interpret(coyo.Value)), interpret)
48+
);
49+
50+
// Runs a Free<T> with the given asynchronous interpreter, returning a Task<T>
51+
public static Task<T> Run<T>
52+
(this Free<T> free, Func<object, Task<object>> interpret)
53+
=> free.Match<Task<T>>(
54+
Done: t => Async(t),
55+
More: async coyo => await Run(coyo.Func(await interpret(coyo.Value)), interpret)
56+
);
57+
58+
/// Lifts a single instruction into a program.
59+
/// Given an instruction such as:
60+
///
61+
/// Ask( Prompt: "What's your age?" )
62+
///
63+
/// we get a Coyo such as
64+
///
65+
/// Coyo(
66+
/// Value: Ask ( Prompt: "What's your age?" ),
67+
/// Func: age => age // where `age` is the value that will be returned by running the Ask
68+
/// )
69+
///
70+
/// and then a program such as:
71+
///
72+
/// More(
73+
/// Coyo(
74+
/// Value: Ask( Prompt: "What's your age?" ),
75+
/// Func: age => Done(
76+
/// Value: age
77+
/// )
78+
/// )
79+
/// )
80+
public static Free<T> Of<T>(object op)
81+
=> More(Coyo.Of<object, T>(op).Map(t => Done<T>(t)));
82+
83+
static Free<T> Done<T>(T t) => new Done<T>(t);
84+
static Free<T> More<T>(Coyo<object, Free<T>> t) => new More<T>(t);
85+
86+
// LINQ operators to be able to use Free with monadic syntax
87+
// Note that in order to make Free a monad we _only_ rely on `Coyo.Map`
88+
// This is the meaning of "free" monad: just based on Coyo being a functor,
89+
// we can define the monad for Free
90+
91+
// this is like a recursive function traversing the list until `f` makes it to the end (Done)
92+
public static Free<R> Select<T, R>(this Free<T> @this
93+
, Func<T, R> func)
94+
=> @this.Match(
95+
Done: t => Done<R>(func(t)),
96+
More: op => More(op.Map(free => free.Select(func)))
97+
);
98+
99+
public static Free<R> SelectMany<T, R>(this Free<T> @this
100+
, Func<T, Free<R>> func)
101+
=> @this.Match(
102+
Done: func,
103+
More: op => More(op.Map(free => free.SelectMany(func)))
104+
);
105+
106+
public static Free<RR> SelectMany<T, R, RR>(this Free<T> @this
107+
, Func<T, Free<R>> bind, Func<T, R, RR> project)
108+
=> SelectMany(@this, t => Select(bind(t), r => project(t, r)));
109+
}
110+
}
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
32
<PropertyGroup>
43
<TargetFramework>netstandard1.6</TargetFramework>
54
<PackageId>LaYumba.Functional</PackageId>
6-
<PackageVersion>0.8.0</PackageVersion>
5+
<PackageVersion>0.9.1</PackageVersion>
76
<PackageProjectUrl>https://github.com/la-yumba/functional-csharp-code</PackageProjectUrl>
87
<PackageLicenseUrl>https://github.com/la-yumba/functional-csharp-code/blob/master/LICENSE</PackageLicenseUrl>
98
<Authors>Enrico Buonanno</Authors>
@@ -12,12 +11,10 @@
1211
<Copyright>Copyright 2017 (c) Enrico Buonanno. All rights reserved.</Copyright>
1312
<PackageTags>functional programming</PackageTags>
1413
</PropertyGroup>
15-
1614
<ItemGroup>
1715
<PackageReference Include="System.Reactive" Version="3.1.1" />
1816
<PackageReference Include="System.Collections.Immutable" Version="1.3.1" />
1917
<PackageReference Include="System.Threading.Tasks.Dataflow" Version="4.7.0" />
2018
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
2119
</ItemGroup>
22-
23-
</Project>
20+
</Project>

0 commit comments

Comments
 (0)