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