1 module frpd.cell.cf;
2 
3 import std.algorithm;
4 import frpd.cell.cell;
5 import frpd.cell._add_listener : addListener,removeListener;
6 import std.typecons:tuple,Tuple;
7 import std.traits : Parameters, ReturnType;
8 import std.meta : staticMap;
9 
10 /**	Create a "cell function" from a normal function.
11 	A cell function is a function that rather than taking values
12 	takes cells as arguments and will return a cell (changing value) of said calculation.
13 	
14 	Cell!<ReturnType> cf(<function>)(Cell!<Parameter>...)
15 */
16 template cf(alias f) {// TODO: better error reporting is f is not of the right type.
17 	private {
18 		alias F = typeof(f);
19 		alias T = ReturnType!F;
20 		alias Params = Parameters!F;
21 		alias CellParams = staticMap!(Cell,Params);
22 		
23 		class FuncCell : Cell!T, CellListener {
24 			import std.meta : staticMap;
25 			
26 			//---Values
27 			T heldValue;
28 			bool heldNeedsUpdate;
29 			Tuple!CellParams cellArgs;	// The Cells from which to extract values from when recalculating.
30 			T delegate(Params) func;	// The function to call with the extracted values.
31 			
32 			//---Constructor
33 			this(	Tuple!CellParams cellArgs,
34 				T delegate(Params) func,
35 			){
36 				heldNeedsUpdate = true;
37 				this.cellArgs = cellArgs;
38 				this.func = func;
39 				
40 				cellArgs.each!(i=>i.addListener(this));
41 			}
42 			~this() {
43 				cellArgs.each!(i=>i.removeListener(this));
44 			}
45 			
46 			//---Methods
47 			/**	Call this "function" to calculate the value.
48 				I might make opCall an alias for this?
49 			*/
50 			override @property T value() {
51 				if (heldNeedsUpdate) {
52 					Tuple!(Params) args;
53 					foreach(i,cellArg; cellArgs) {
54 						args[i] = cellArg.value;
55 					}
56 					heldValue = func(args.expand);
57 					heldNeedsUpdate = false;
58 				}
59 				return heldValue;
60 			}
61 			
62 			//---Listener methods
63 			override void onValueReady() {
64 				heldNeedsUpdate = true;
65 				super.onValueReady;
66 			}
67 			override void push() {
68 				super.push;
69 			}
70 			
71 		}
72 	}
73 	Cell!T cf(CellParams cellArgs) {
74 		return new FuncCell(cellArgs.tuple, (Params args){return f(args);});
75 	};
76 }
77 
78 
79 unittest {
80 	import frpd.cell.settable_cell : cell;
81 	int mul(int l, int r) {
82 		return l*r;
83 	}
84 	
85 	//---
86 	auto a = cell!int(1);
87 	auto b = cell(2);
88 	auto c = cf!mul(a,b);
89 	{
90 		import frpd.cell : Cell;
91 		assert(is(typeof(c)==Cell!int));
92 	}
93 	
94 	assert(c.value==2);
95 	
96 	a.value = 2;
97 	assert(c.value==4);
98 	b.value = 3;
99 	assert(c.value==6);
100 	
101 	//---
102 	import std.conv : to;
103 	auto d = cf!((int v)=>v.to!string)(c);
104 	
105 	assert(d.value=="6");
106 	a.value = 12;
107 	assert(d.value=="36");
108 	
109 	//---
110 	import std.range : repeat;
111 	import std.array : join;
112 	auto e = cf!(	(string s, int t) {
113 			return s.repeat(t).join;
114 		}
115 	)(d, b);
116 	
117 	assert(e.value=="363636");
118 	a.value = 1;
119 	assert(e.value=="333");
120 }
121