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
+ }
0 commit comments