Overview
SHA1 Hash: | b993a8ad168487e4bd482a1fbb2ff69ce7e010f9 |
---|---|
Date: | 2010-11-25 02:44:58 |
User: | kinaba |
Comment: | auto memo and re-run feature of non @value/@macro layers re-re-re-implemented. |
Timelines: | family | ancestors | descendants | both | trunk |
Downloads: | Tarball | ZIP archive |
Other Links: | files | file ages | manifest |
Tags And Properties
- branch=trunk inherited from [f65680e1d2]
- sym-trunk inherited from [f65680e1d2]
Changes
Modified polemy/eval.d from [197c2cfb75326106] to [bc3c32f1d9aafc10].
256 override Table definitionContext() { return defCtx; } 256 override Table definitionContext() { return defCtx; } 257 257 258 this(Fun ast, Table defCtx) { this.ast=ast; this.defCtx= 258 this(Fun ast, Table defCtx) { this.ast=ast; this.defCtx= 259 override string toString() const 259 override string toString() const 260 { return sprintf!"(function:%x:%x)"(cast(void*)a 260 { return sprintf!"(function:%x:%x)"(cast(void*)a 261 override int opCmp(Object rhs) { 261 override int opCmp(Object rhs) { 262 if(auto r = cast(UserDefinedFunValue)rhs) { 262 if(auto r = cast(UserDefinedFunValue)rhs) { 263 auto a = cast(void*)this.ast; < 264 auto b = cast(void*)r.ast; | 263 if(auto c = typeid(void*).compare(cast(v 265 if(a<b) return -1; | 264 return c; > 265 if(auto c = typeid(void*).compare(cast(v > 266 return c; 266 if(a>b) return +1; // [TODO] avoid using | 267 return 0;// [TODO] avoid using pointer v 267 return this.defCtx.opCmp(r.defCtx); < 268 } 268 } 269 if(auto r = cast(Value)rhs) return typeid(this). 269 if(auto r = cast(Value)rhs) return typeid(this). 270 throw genex!RuntimeException("comparison with va 270 throw genex!RuntimeException("comparison with va 271 } 271 } 272 mixin SimpleToHash; | 272 override hash_t toHash() { > 273 return (cast(hash_t)cast(void*)ast) + (cast(hash > 274 } 273 275 274 AST afterMacroAST; | 276 AST macroCache; > 277 static class MemokeyType > 278 { > 279 void* a; Layer b; Tuple!(string,Layer,Value)[] c > 280 hash_t toHash() { > 281 hash_t h = structuralHash(a) + structura > 282 foreach(e; c) > 283 h += structuralHash(e[0])+struct > 284 return h; > 285 } > 286 mixin SimpleToString; > 287 mixin SimpleConstructor; > 288 mixin SimpleCompareWithoutToHash; > 289 } > 290 static Tuple!(Value,int)[MemokeyType] memo; > 291 275 override Value invoke(Layer lay, Table ctx, LexPosition 292 override Value invoke(Layer lay, Table ctx, LexPosition 276 { 293 { 277 if( isASTLayer(lay) ) 294 if( isASTLayer(lay) ) 278 return eval(ast.funbody, lay, ctx); 295 return eval(ast.funbody, lay, ctx); > 296 > 297 auto nonMemoizedRun = (){ 279 if( afterMacroAST is null ) | 298 if( macroCache is null ) > 299 { > 300 auto va = macroAndEval(e.funbody > 301 macroCache = va[1]; > 302 return va[0]; > 303 } > 304 else > 305 return eval(macroCache, lay, ctx > 306 }; > 307 > 308 if( !isUserDefinedLayer(lay) ) > 309 return nonMemoizedRun(); > 310 > 311 MemokeyType memokey = new MemokeyType(cast(void* > 312 > 313 if(auto p = memokey in memo) 280 { 314 { 281 auto va = macroAndEval(e.funbody, lay, c | 315 (*p)[1] ++; 282 afterMacroAST = va[1]; < 283 return va[0]; | 316 return (*p)[0]; 284 } 317 } 285 else 318 else 286 return eval(afterMacroAST, lay, ctx); | 319 memo[memokey] = tuple(lift(new Undefined > 320 > 321 Value r = nonMemoizedRun(); > 322 > 323 int touched = memo[memokey][1]; > 324 memo[memokey] = tuple(r, 12345678); > 325 //if(touched) {DBG("rerun :: ",r);r = nonMemoize > 326 return r; 287 } 327 } 288 } 328 } 289 return new UserDefinedFunValue(e,ctx); 329 return new UserDefinedFunValue(e,ctx); 290 } 330 } 291 331 292 public: 332 public: 293 /// Add primitive function to the global context 333 /// Add primitive function to the global context ................................................................................................................................................................................ 300 340 301 override string toString() { return sprintf!"(native:%x) 341 override string toString() { return sprintf!"(native:%x) 302 override int opCmp(Object rhs) { 342 override int opCmp(Object rhs) { 303 if(auto r = cast(NativeFunValue)rhs) return type 343 if(auto r = cast(NativeFunValue)rhs) return type 304 if(auto r = cast(Value)rhs) return type 344 if(auto r = cast(Value)rhs) return type 305 throw genex!RuntimeException("comparison with va 345 throw genex!RuntimeException("comparison with va 306 } 346 } 307 mixin SimpleToHash; | 347 override hash_t toHash() const { > 348 return typeid(dg).getHash(&dg); > 349 } 308 350 309 R delegate(T) dg; 351 R delegate(T) dg; 310 Parameter[] params_data; 352 Parameter[] params_data; 311 353 312 this(R delegate(T) dg) 354 this(R delegate(T) dg) 313 { 355 { 314 this.dg = dg; 356 this.dg = dg;
Modified polemy/failure.d from [2827b7df74d1df19] to [442c0afd591f3c6a].
49 template ExceptionWithPosition() 49 template ExceptionWithPosition() 50 { 50 { 51 LexPosition pos; 51 LexPosition pos; 52 this( LexPosition pos, string msg, string file=null, size_t line=0, Thro 52 this( LexPosition pos, string msg, string file=null, size_t line=0, Thro 53 { 53 { 54 string fullmsg = pos is null ? sprintf!("\n[??] %s")(msg) 54 string fullmsg = pos is null ? sprintf!("\n[??] %s")(msg) 55 : sprintf!("\n[%s] %s")(pos, msg); 55 : sprintf!("\n[%s] %s")(pos, msg); 56 for(int i=0; i<callstack_pos.length || i<callstack_msg.length; + | 56 foreach_reverse(i,_; callstack_pos) 57 { 57 { 58 LexPosition p = (i<callstack_pos.length ? callstack_pos[ | 58 LexPosition p = callstack_pos[i]; 59 string m = (i<callstack_msg.length ? callstack_msg[ | 59 string m = callstack_msg[i]; 60 fullmsg ~= p is null ? sprintf!("\n[??] %s")(m) 60 fullmsg ~= p is null ? sprintf!("\n[??] %s")(m) 61 : sprintf!("\n[%s] %s")(p, m); 61 : sprintf!("\n[%s] %s")(p, m); 62 } 62 } 63 super(fullmsg, file, line, next); 63 super(fullmsg, file, line, next); 64 this.pos = pos; 64 this.pos = pos; 65 } 65 } 66 this( string msg, string file=null, size_t line=0, Throwable next=null ) 66 this( string msg, string file=null, size_t line=0, Throwable next=null )
Modified polemy/value.d from [dc69a053821951bd] to [6fa9c2c94c01b274].
18 } 18 } 19 19 20 /// 20 /// 21 class IntValue : Value 21 class IntValue : Value 22 { 22 { 23 BigInt data; 23 BigInt data; 24 24 25 this(bool n) { this.data = n?1:0; } | 25 this(bool n) { this.data = n?1:0; } 26 this(int n) { this.data = n; } | 26 this(int n) { this.data = n; } 27 this(long n) { this.data = n; } | 27 this(long n) { this.data = n; } 28 this(BigInt n) { this.data = n; } 28 this(BigInt n) { this.data = n; } 29 this(string n) { this.data = BigInt(n); } 29 this(string n) { this.data = BigInt(n); } 30 override string toString() const { return toDecimalString(cast(BigInt)da 30 override string toString() const { return toDecimalString(cast(BigInt)da 31 override int opCmp(Object rhs) { 31 override int opCmp(Object rhs) { 32 if(auto r = cast(IntValue)rhs) return data.opCmp(r.data); 32 if(auto r = cast(IntValue)rhs) return data.opCmp(r.data); 33 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid( 33 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid( 34 throw genex!RuntimeException("comparison with value and somithin 34 throw genex!RuntimeException("comparison with value and somithin ................................................................................................................................................................................ 79 class Table : Value 79 class Table : Value 80 { 80 { 81 enum Kind {PropagateSet, NotPropagateSet}; 81 enum Kind {PropagateSet, NotPropagateSet}; 82 82 83 this( Table proto=null, Kind k = Kind.PropagateSet ) 83 this( Table proto=null, Kind k = Kind.PropagateSet ) 84 { this.prototype = proto; this.kind = k; } 84 { this.prototype = proto; this.kind = k; } 85 85 > 86 /// Set the value v to the index i of layer lay 86 void set(string i, Layer lay, Value v) 87 void set(string i, Layer lay, Value v) 87 { 88 { 88 if( setIfExist(i, lay, v) ) 89 if( setIfExist(i, lay, v) ) 89 return; 90 return; 90 data[i][lay] = v; 91 data[i][lay] = v; 91 } 92 } 92 93 > 94 /// True if index i has value in layer lay 93 bool has(string i, Layer lay) const 95 bool has(string i, Layer lay) const 94 { 96 { 95 if( i in data ) 97 if( i in data ) 96 return !!(lay in data[i]); 98 return !!(lay in data[i]); 97 if( prototype is null ) 99 if( prototype is null ) 98 return false; 100 return false; 99 return prototype.has(i, lay); 101 return prototype.has(i, lay); 100 } 102 } 101 103 > 104 /// Return the value of index i at layer lay. Throws if it is not set 102 Value get(string i, Layer lay, LexPosition pos=null) 105 Value get(string i, Layer lay, LexPosition pos=null) 103 { 106 { 104 if( i in data ) { 107 if( i in data ) { 105 // [TODO] consider forwarding to proto also in this case 108 // [TODO] consider forwarding to proto also in this case 106 if( lay !in data[i] ) 109 if( lay !in data[i] ) 107 throw genex!RuntimeException(pos, sprintf!"'%s' 110 throw genex!RuntimeException(pos, sprintf!"'%s' 108 return data[i][lay]; 111 return data[i][lay]; 109 } 112 } 110 if( prototype is null ) 113 if( prototype is null ) 111 throw genex!RuntimeException(pos, sprintf!"'%s' not foun 114 throw genex!RuntimeException(pos, sprintf!"'%s' not foun 112 return prototype.get(i, lay, pos); 115 return prototype.get(i, lay, pos); 113 } 116 } 114 117 > 118 /// t.access!T(lay,a,b,...) returns t.get(a,lay).get(b,lay).... if exist > 119 /// and has type T. Returns null otherwise 115 T access(T,S...)( Layer lay, string path, S rest ) 120 T access(T,S...)( Layer lay, string path, S rest ) 116 { 121 { 117 static if( rest.length == 0 ) 122 static if( rest.length == 0 ) 118 { 123 { 119 if( this.has(path, lay) ) 124 if( this.has(path, lay) ) 120 return cast(T) this.get(path, lay); 125 return cast(T) this.get(path, lay); 121 } 126 } ................................................................................................................................................................................ 123 { 128 { 124 if(auto next = this.access!Table(lay,path)) 129 if(auto next = this.access!Table(lay,path)) 125 return next.access!T(lay,rest); 130 return next.access!T(lay,rest); 126 } 131 } 127 return null; 132 return null; 128 } 133 } 129 134 130 string toStringWithoutParen() const < 131 { < 132 string result; < 133 bool first = true; < 134 foreach(k, l2d; data) < 135 foreach(l,d; l2d) < 136 { < 137 if(first) first=false; else result~=", "; < 138 result ~= k; < 139 if( l.empty ) < 140 result ~= "(emptylayer)"; < 141 else if( l != ValueLayer ) < 142 result ~= l; < 143 result ~= ":"; < 144 result ~= text(cast(Value)d); < 145 } < 146 if( prototype !is null ) < 147 { < 148 result ~= " / "; < 149 result ~= prototype.toStringWithoutParen(); < 150 } < 151 return result; < 152 } < 153 < 154 string toString() < 155 { < 156 if( isList() ) < 157 return text(toList()); < 158 return "{" ~ toStringWithoutParen() ~ "}"; < 159 } < 160 < 161 public: < 162 /// Is this an empty table? 135 /// Is this an empty table? 163 bool empty() 136 bool empty() 164 { 137 { 165 return data.length==0 && (prototype is null || prototype.empty); 138 return data.length==0 && (prototype is null || prototype.empty); 166 } 139 } 167 140 168 /// Can be seen as a cons-list? 141 /// Can be seen as a cons-list? ................................................................................................................................................................................ 190 else 163 else 191 throw genex!RuntimeException("this table is not 164 throw genex!RuntimeException("this table is not 192 } 165 } 193 if( t.empty ) 166 if( t.empty ) 194 return result; 167 return result; 195 throw genex!RuntimeException("this table is not a cons-list"); 168 throw genex!RuntimeException("this table is not a cons-list"); 196 } 169 } > 170 > 171 /// Get the list of direct entries ignoring prototypes in sorted order > 172 Tuple!(string,Layer,Value)[] direct_entries() > 173 { > 174 Tuple!(string,Layer,Value)[] arr; > 175 foreach(k, l2d; data) > 176 foreach(l,d; l2d) > 177 arr ~= tuple(k,l,d); > 178 arr.sort(); > 179 return arr; > 180 } > 181 > 182 /// Get the whole list of observable entries in unspecified order > 183 Tuple!(string,Layer,Value)[] entries() > 184 { > 185 bool[string] hidden; > 186 Tuple!(string,Layer,Value)[] arr; > 187 enumerateEntries(hidden, arr); > 188 return arr; > 189 } > 190 > 191 private void enumerateEntries( ref bool[string] hidden, ref Tuple!(strin > 192 { > 193 foreach(k, l2d; data) > 194 if( k !in hidden ) > 195 { > 196 foreach(l,d; l2d) > 197 arr ~= tuple(k,l,d); > 198 hidden[k] = true; > 199 } > 200 if(prototype !is null) > 201 prototype.enumerateEntries(hidden, arr); > 202 } > 203 > 204 override string toString() > 205 { > 206 if( isList() ) > 207 return text(toList()); > 208 return "{" ~ toStringWithoutParen() ~ "}"; > 209 } > 210 > 211 override int opCmp(Object rhs) > 212 { > 213 if(auto r = cast(Table)rhs) { > 214 Tuple!(string,Layer,Value)[] ls = this.entries(); > 215 Tuple!(string,Layer,Value)[] rs = r.entries(); > 216 if( ls.length != rs.length ) > 217 return (ls.length < rs.length ? -1 : +1); > 218 ls.sort(); > 219 rs.sort(); > 220 foreach(i,_; ls) > 221 if(auto c = ls[i].opCmp(rs[i])) > 222 return c; > 223 return 0; > 224 } > 225 if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)) > 226 throw genex!RuntimeException("comparison with value and somithin > 227 } > 228 > 229 override hash_t toHash() > 230 { > 231 Tuple!(string,Layer,Value)[] ls = this.entries(); > 232 ls.sort(); > 233 hash_t h; > 234 foreach(e; ls) > 235 h += structuralHash(e[0])+structuralHash(e[1])+structura > 236 return h; > 237 } 197 238 198 private: 239 private: 199 Table prototype; 240 Table prototype; 200 Kind kind; 241 Kind kind; 201 Value[Layer][string] data; 242 Value[Layer][string] data; > 243 > 244 string toStringWithoutParen() const > 245 { > 246 string result; > 247 bool first = true; > 248 foreach(k, l2d; data) > 249 foreach(l,d; l2d) > 250 { > 251 if(first) first=false; else result~=", "; > 252 result ~= k; > 253 if( l.empty ) > 254 result ~= "(emptylayer)"; > 255 else if( l != ValueLayer ) > 256 result ~= l; > 257 result ~= ":"; > 258 result ~= text(cast(Value)d); > 259 } > 260 if( prototype !is null ) > 261 { > 262 result ~= " / "; > 263 result ~= prototype.toStringWithoutParen(); > 264 } > 265 return result; > 266 } 202 267 203 bool setIfExist(string i, Layer lay, Value v) 268 bool setIfExist(string i, Layer lay, Value v) 204 { 269 { 205 if( i in data ) 270 if( i in data ) 206 { 271 { 207 data[i][lay] = v; 272 data[i][lay] = v; 208 return true; 273 return true;
Modified sample/type.pmy from [f80a12af3228ea33] to [12b974146da19906].
20 def mergeType(a,b) { 20 def mergeType(a,b) { 21 if( a == "undefined" ): ( if(b=="undefined"):"error" else b ) else ( a ) 21 if( a == "undefined" ): ( if(b=="undefined"):"error" else b ) else ( a ) 22 }; 22 }; 23 23 24 @type "if" = fun(c,t,e) {@value( 24 @type "if" = fun(c,t,e) {@value( 25 if(@type(c)=="int" ): mergeType(@type(t()), @type(e())) else : "error" 25 if(@type(c)=="int" ): mergeType(@type(t()), @type(e())) else : "error" 26 )}; 26 )}; > 27 > 28 def fib(x) > 29 { > 30 if x<2 then 1 else fib(x-1) + fib(x-2) > 31 }; > 32 > 33 print( @type(fib(10)) );
Modified tricks/tricks.d from [cc1b586b87325960] to [00969146488cc3dc].
95 // shiyo- desu. Don't use in this way. 95 // shiyo- desu. Don't use in this way. 96 // Tamp tries to call new Tomp(real) (because it only sees Tomp's memb 96 // Tamp tries to call new Tomp(real) (because it only sees Tomp's memb 97 // but it fails because Tomp takes (int,string,real). 97 // but it fails because Tomp takes (int,string,real). 98 assert( !__traits(compiles, { 98 assert( !__traits(compiles, { 99 class Tamp : Tomp { mixin SimpleConstructor; } 99 class Tamp : Tomp { mixin SimpleConstructor; } 100 }) ); 100 }) ); 101 } 101 } > 102 > 103 hash_t structuralHash(T)(T x) > 104 { > 105 alias SC_Unqual!(T) UCT; > 106 > 107 static if(is(UCT == class)) > 108 return (cast(UCT)x).toHash(); > 109 else > 110 static if(SC_HasGoodHash!(UCT)) > 111 { return typeid(UCT).getHash(&x); } > 112 else > 113 static if(is(UCT T == T[])) > 114 { hash_t h; foreach(e; x) h+=structuralHash(e); return h; } > 115 else > 116 static if(is(UCT == struct)) > 117 static if(__traits(compiles, std.bigint.BigInt)) > 118 static if(is(UCT == std.bigint.BigInt)) > 119 return cast(hash_t) x.toInt(); > 120 else > 121 static assert(false, "should not use struct.toHa > 122 else > 123 static assert(false, "should not use struct.toHash"); > 124 else > 125 static assert(false, "nonhashable datatype "~UCT.stringof); > 126 } > 127 > 128 alias std.traits.Unqual SC_Unqual; > 129 > 130 template SC_HasGoodHash(T) > 131 { > 132 enum SC_HasGoodHash = > 133 is(T : bool) || isNumeric!(T) || isSomeString!(T) || isSomeChar! > 134 } 102 135 103 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class 136 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class > 137 /// BE SURE THAT THIS IS CONSISTENT WITH opCmp and opEquals 104 138 105 template SimpleToHash() 139 template SimpleToHash() 106 { 140 { 107 override hash_t toHash() const /// member-by-member hash 141 override hash_t toHash() const /// member-by-member hash 108 { 142 { 109 hash_t h = 0; 143 hash_t h = 0; 110 foreach(mem; this.tupleof) 144 foreach(mem; this.tupleof) 111 h += typeid(mem).getHash(&mem); | 145 h += structuralHash(mem); 112 return h; 146 return h; 113 } 147 } 114 } 148 } > 149 > 150 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class > 151 > 152 template SimpleCompareWithoutToHash() > 153 { > 154 override bool opEquals(Object rhs) const /// member-by-member equality > 155 { > 156 return opCmp(rhs) == 0; > 157 } > 158 > 159 override int opCmp(Object rhs_) const /// member-by-member compare > 160 { > 161 if( rhs_ is null ) > 162 return -1; > 163 if( auto rhs = cast(typeof(this))rhs_ ) > 164 { > 165 foreach(i,_; this.tupleof) > 166 { > 167 static if(is(typeof(_) == struct)) > 168 auto c = (cast(SC_Unqual!(typeof(_)))thi > 169 else > 170 auto c = typeid(_).compare(&this.tupleof > 171 if(c) > 172 return c; > 173 } > 174 return 0; > 175 } > 176 return typeid(this).opCmp(typeid(rhs_)); > 177 } > 178 } 115 179 116 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class 180 /// Mixing-in the MOST-DERIVED-member-wise comparator for a class 117 181 118 /*mixin*/ 182 /*mixin*/ 119 template SimpleCompare() 183 template SimpleCompare() 120 { 184 { 121 override bool opEquals(Object rhs_) const /// member-by-member equality < 122 { < 123 if( auto rhs = cast(typeof(this))rhs_ ) < 124 { < 125 foreach(i,_; this.tupleof) < 126 if( this.tupleof[i] != (cast(const)rhs).tupleof[ < 127 return false; < 128 return true; < 129 } < 130 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), < 131 } < 132 < 133 mixin SimpleToHash; 185 mixin SimpleToHash; 134 < 135 override int opCmp(Object rhs_) const /// member-by-member compare < 136 { < 137 if( auto rhs = cast(typeof(this))rhs_ ) < 138 { < 139 foreach(i,_; this.tupleof) < 140 if( this.tupleof[i] != (cast(const)rhs).tupleof[ < 141 auto a = (cast(SC_Unqual!(typeof(this))) < 142 auto b = rhs.tupleof[i]; < 143 return a < b ? -1 : +1; < 144 } < 145 // not work well for structures??????? < 146 // if(auto c = typeid(_).compare(&this.tupleof[i],& < 147 // return c; < 148 return 0; < 149 } < 150 assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), < 151 } < > 186 mixin SimpleCompareWithoutToHash; 152 } 187 } 153 188 154 alias std.traits.Unqual SC_Unqual; < 155 < 156 unittest 189 unittest 157 { 190 { 158 class Temp 191 class Temp 159 { 192 { 160 int x; 193 int x; 161 string y; 194 string y; 162 mixin SimpleConstructor; 195 mixin SimpleConstructor; ................................................................................................................................................................................ 173 class TempDummy 206 class TempDummy 174 { 207 { 175 int x; 208 int x; 176 string y; 209 string y; 177 mixin SimpleConstructor; 210 mixin SimpleConstructor; 178 mixin SimpleCompare; 211 mixin SimpleCompare; 179 } 212 } 180 assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") ); | 213 assert_ne( new Temp(1,"foo"), new TempDummy(1,"foo") ); 181 assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") ); | 214 assert_nothrow( new Temp(1,"foo") <= new TempDummy(1,"foo") ); 182 } 215 } 183 216 184 /// Mixing-in a simple toString method 217 /// Mixing-in a simple toString method 185 218 186 /*mixin*/ 219 /*mixin*/ 187 template SimpleToString() 220 template SimpleToString() 188 { 221 { ................................................................................................................................................................................ 222 template SimpleClass() 255 template SimpleClass() 223 { 256 { 224 mixin SimpleConstructor; 257 mixin SimpleConstructor; 225 mixin SimpleCompare; 258 mixin SimpleCompare; 226 mixin SimpleToString; 259 mixin SimpleToString; 227 } 260 } 228 261 229 /// Will be used for dynamic overload resolution pattern | 262 /// Utility 230 263 231 template firstParam(T) 264 template firstParam(T) 232 { 265 { 233 alias ParameterTypeTuple!(T)[0] firstParam; 266 alias ParameterTypeTuple!(T)[0] firstParam; 234 } 267 }