digitalmars.D - Metaprog can be abstruse
- user1234 (27/27) Jul 04 2022 Example:
- Bastiaan Veelo (6/33) Jul 04 2022 Not necessarily.
- user1234 (3/8) Jul 04 2022 Hi, to this alternative I'd say that it's not self documenting.
- Bastiaan Veelo (11/20) Jul 04 2022 I can see your point, but it depends on how used you are to
- Dom Disc (8/19) Jul 04 2022 This advantage becomes much more clear in, let's say:
- bauss (12/21) Jul 04 2022 I think that depends on your expertise. It's self-document if you
- drug007 (3/15) Jul 04 2022 As for me it is self documenting. And it is more readable than your hand...
- kdevel (59/64) Jul 04 2022 Your example requires
- Alexandru Ermicioi (2/8) Jul 05 2022 To be fair, original example also requires some testing.
- kdevel (21/22) Jul 05 2022 Sure, but user1234 was never in favor of it. It is prone to the
- Dom Disc (30/44) Dec 28 2022 Sorry for the late reply, but I just stumbled over this old
- bauss (22/49) Jul 04 2022 If you don't like the iota version then you can just do this:
- user1234 (3/27) Jul 04 2022 still the same problems as the initial version (even if yes I
- bauss (11/13) Jul 04 2022 I'm actually not sure what you're even trying to achieve or what
- ryuukk_ (9/9) Jul 04 2022 I personally only use metaprogramming for tasks i will know will
Example: ```d auto genDecimalRanks() { import std.conv; auto r = 1; auto result = "["; foreach (i; 1 .. 11) { result ~= to!string(r); if (i < 10) result ~= ", "; r *= 10; } result ~= "]"; return result; } /// like [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] immutable decimalRanks = mixin(genDecimalRanks); ``` It takes much more space and time to write DDOC + the generator than the space + time required to write the equivalent explicit declaration without comments, i.e "self documenting". So metaprog is not the panacea, do you think to that before "meta-progrogramming", or do you "meta-prog" just because it's nice ?
Jul 04 2022
On Monday, 4 July 2022 at 08:24:01 UTC, user1234 wrote:Example: ```d auto genDecimalRanks() { import std.conv; auto r = 1; auto result = "["; foreach (i; 1 .. 11) { result ~= to!string(r); if (i < 10) result ~= ", "; r *= 10; } result ~= "]"; return result; } /// like [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] immutable decimalRanks = mixin(genDecimalRanks); ``` It takes much more space and time to write DDOC + the generator than the space + time required to write the equivalent explicit declaration without comments, i.e "self documenting". So metaprog is not the panacea, do you think to that before "meta-progrogramming", or do you "meta-prog" just because it's nice ?Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ``` -- Bastiaan.
Jul 04 2022
On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ``` -- Bastiaan.Hi, to this alternative I'd say that it's not self documenting. So it still has 50% of the initial problem.
Jul 04 2022
On Monday, 4 July 2022 at 09:43:11 UTC, user1234 wrote:On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:I can see your point, but it depends on how used you are to reading chains of range algorithms. I read this as "given the integers 0 to 9, use them as the powers of ten and put them in an array". There is a point to be made that this is more self-documenting than the hand-written alternative: Here it is obvious that the ranks cover all powers of 10 from 0 to 9 in order. In the hand-written alternative you'd either assume that is the case, or have to meticulously count all the zero's to be sure. -- Bastiaan.Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ``` -- Bastiaan.Hi, to this alternative I'd say that it's not self documenting. So it still has 50% of the initial problem.
Jul 04 2022
On Monday, 4 July 2022 at 10:19:36 UTC, Bastiaan Veelo wrote:On Monday, 4 July 2022 at 09:43:11 UTC, user1234 wrote:This advantage becomes much more clear in, let's say: ```d immutable long powersOfThree = iota(27).map!(p => 3.pow(p)).array; ``` Handwritten this is a very long chain of arbitrary numbers and you have to put a very close look, that those are indeed the powers of three.On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:There is a point to be made that this is more self-documenting than the hand-written alternative: Here it is obvious that the ranks cover all powers of 10 from 0 to 9 in order.Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ```Hi, to this alternative I'd say that it's not self documenting. So it still has 50% of the initial problem.
Jul 04 2022
On Monday, 4 July 2022 at 13:03:55 UTC, Dom Disc wrote: [...]This advantage becomes much more clear in, let's say: ```d immutable long powersOfThree = iota(27).map!(p => 3.pow(p)).array; ``` Handwritten this is a very long chain of arbitrary numbers and you have to put a very close look, that those are indeed the powers of three.Really? ``` immutable long [] powersOfThree = [ 1, 3, 3 * 3, 3 * 3 * 3, 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, ]; ```
Jul 05 2022
On Wednesday, 6 July 2022 at 00:11:58 UTC, kdevel wrote:On Monday, 4 July 2022 at 13:03:55 UTC, Dom Disc wrote: [...]Ok, but with this, you can't see where the overflow will occur. So you have the same problem as with the one-liner (and it is indeed very much longer).This advantage becomes much more clear in, let's say: ```d immutable long powersOfThree = iota(27).map!(p => 3.pow(p)).array; ``` Handwritten this is a very long chain of arbitrary numbers and you have to put a very close look, that those are indeed the powers of three.Really? ``` immutable long [] powersOfThree = [ 1, 3, 3 * 3, 3 * 3 * 3, 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3, ]; ```
Jul 06 2022
On Monday, 4 July 2022 at 09:43:11 UTC, user1234 wrote:On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:I think that depends on your expertise. It's self-document if you understand the functionality of iota, map, pow and array. Saying it's not self documenting is the same as saying: `writeln("Hello World!")` isn't self documenting, but I do believe we can both agree on that it pretty much is. It's self documenting for the same reason, you understand the functionality behind writeln and thus knows what it is doing. The same can be said about the above function chain. It's very much straight-forward in what it's doing and it's only when one doesn't understand these "basic" functions that one don't find it self-documenting.Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ``` -- Bastiaan.Hi, to this alternative I'd say that it's not self documenting. So it still has 50% of the initial problem.
Jul 04 2022
On 7/4/22 12:43, user1234 wrote:On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:As for me it is self documenting. And it is more readable than your hand written imperative version.Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ``` -- Bastiaan.Hi, to this alternative I'd say that it's not self documenting. So it still has 50% of the initial problem.
Jul 04 2022
On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:[...] Not necessarily. ```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ```Your example requires ``` import std.range; // for iota import std.algorithm; // for map import std.math; // for pow of int ``` Then your example is prone to errors: ``` immutable decimalRanks = iota(13).map!(p => 10.pow(p)).array; // silent wrap around ``` (unit)test to the rescue: ``` auto make_decimal_rank () { import std.range; import std.algorithm; import std.math; return iota (13).map!(p => 10.pow (p)).array; } void main () { import std.stdio; immutable decimalRank = make_decimal_rank; decimalRank.writeln; typeof (decimalRank).stringof.writeln; } unittest { enum ar13 = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000 ]; pragma (msg, typeof (ar13)); assert (make_decimal_rank == ar13); } ``` Running: ``` dmd -checkaction=context -unittest -run decrank long[] decrank.d(35): [unittest] [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1410065408, 1215752192, -727379968] != [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000] 1/1 modules FAILED unittests ```
Jul 04 2022
On Monday, 4 July 2022 at 23:19:27 UTC, kdevel wrote:Then your example is prone to errors: ``` immutable decimalRanks = iota(13).map!(p => 10.pow(p)).array; // silent wrap around ``` (unit)test to the rescue:To be fair, original example also requires some testing.
Jul 05 2022
On Tuesday, 5 July 2022 at 07:37:35 UTC, Alexandru Ermicioi wrote: [...]To be fair, original example also requires some testing.Sure, but user1234 was never in favor of it. It is prone to the same wrap around which have been avoided by using strings in the first place: ``` auto sgenDecimalRanks() { auto r = "1"; auto result = "["; foreach (i; 1 .. 13) { result ~= r; if (i < 12) result ~= ", "; r ~= '0'; } result ~= "]"; return result; } ```
Jul 05 2022
On Monday, 4 July 2022 at 23:19:27 UTC, kdevel wrote:Sorry for the late reply, but I just stumbled over this old thread. I now have a function ```d ubyte maxpow(const ulong base) { } ``` That returns the maximum power of the given base that fits in a ucent (for smaller target types, shift down the result by one for each halfing of the size). With this you can create the function ```d ulong[] powersOf(byte n) { import std.range : iota; import std.algorithm : map; return iota(maxpow(n)>>1).map!(p => ulong(n)^^p).array; } ``` If this function is used to create lookup-tables, it will be called only during compile-time, so the imports are not in the resulting executable (therefore it doesn't matter that I replaced pow by the buildin ^^). ```d static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17); ``` I think this is much more readable as a list of obscure number literals, it is safe to use, doesn't increase linker time and doesn't blow up the binary. Hurray to the meta-programming! Hurray! Hurray!```d immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array; ```This example requires ```d import std.range; // for iota import std.algorithm; // for map import std.math; // for pow of int ``` Then your example is prone to errors: ```d immutable decimalRanks = iota(13).map!(p => 10.pow(p)).array; // silent wrap around ```
Dec 28 2022
On Wednesday, 28 December 2022 at 13:57:13 UTC, Dom Disc wrote:[...] I now have a function ```d ubyte maxpow(const ulong base) { } ```Code?That returns the maximum power of the given base that fits in a ucentucent? Cannot use cent/ucent or core.int128's types here.(for smaller target types, shift down the result by one for each halfing of the size).What do you mean by "shift down"? "Shift right" aka "divide by two"?With this you can create the function ```d ulong[] powersOf(byte n) { import std.range : iota; import std.algorithm : map; return iota(maxpow(n)>>1).map!(p => ulong(n)^^p).array; } ```This generates too few elements. ``` import std.stdio; import mod_maxpow; auto powersOf (T) (ulong n) { import std.range : iota; import std.algorithm : map; import std.array : array; return iota(maxpow!T (n) + 1).map!(p => n^^p).array; } int main (string [] args) { static immutable ranksOf17 = powersOf!ulong (17); writeln (ranksOf17); return 0; } ``` prints ``` [1, 17, 289, 4913, 83521, 1419857, 24137569, 410338673, 6975757441, 118587876497, 2015993900449, 34271896307633, 582622237229761, 9904578032905937, 168377826559400929, 2862423051509815793] ``` `maxpow!ulong(17)` is 15. Counting starts with 0 so we must add one.If this function is used to create lookup-tables, it will be called only during compile-time, so the imports are not in the resulting executable (therefore it doesn't matter that I replaced pow by the buildin ^^). ```d static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17); ```Unnecessary code duplication.I think this is much more readable as a list of obscure number literals, it is safe to use,Unit tests?
Jan 01 2023
On Sunday, 1 January 2023 at 23:05:46 UTC, kdevel wrote:On Wednesday, 28 December 2022 at 13:57:13 UTC, Dom Disc wrote:Yeah, I implemented this long time ago, when I still had hope that ucent will be available soon :-/[...] I now have a function ```d ubyte maxpow(const ulong base) { } ```Code?That returns the maximum power of the given base that fits in a ucentucent? Cannot use cent/ucent or core.int128's types here.Yes. Divide by two to find out what power fits in uint, divide by four for ushort and divide by 8 for ubyte.(for smaller target types, shift down the result by one for each halfing of the size).What do you mean by "shift down"? "Shift right" aka "divide by two"?[...] This generates too few elements.Ok, but I tend to omit n^^0 and n^^1 - so I would say it even generates too many elements :-)Yes, I hate that too. But ```d ulong[] ranksOf17 = powersOf(17); ``` will generate a dynamic array, and unfortunately an inherited length for static arrays (e.g. something like uint[$] x = [1,2,3]; or whatever syntax we could agree upon) is still not available :-(```d static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17); ```Unnecessary code duplication.Unit tests?Of course. Nothing that you can do will ever make those obsolete. Mine are rather exhaustive: ```d /// exponentiation without overflow /// return invalid if overflow would occur. T safePow(T)(const(T) base, const int exp) if(isIntegral!T) { if(exp < 2) return !exp ? 1 : (exp < 0 && base.abs != 1) ? 0 : odd(exp) ? base : 1; static if(isUnsigned!T) { if(base < 2) return base; if(base >= 1<<(T.sizeof<<2)) return invalid!T; // base too large to fit any powers of it into the same type if(exp > (maxpow(base)>>(5-bitlen(T.sizeof)))) return invalid!T; return T(base^^exp); // calc only if no overflow for sure (very efficient) } else { auto r = safePow(abs(base), exp); // max power may not fit in the signed type if(r > T.max) return invalid!T; // but at least we can detect it return (base < 0 && odd(exp)) ? -T(r) : T(r); } } unittest { import std.conv; void test(T)() { T r; foreach(T base; (-128 * isSigned!T)..128) { foreach(uint exp; 0..256) { r = safePow(base, exp); if(exp==1) assert(r == base); else if(r == invalid!T) { assert(T.max / safePow(base, exp-1).abs < base.abs, "max wrong: ",base,"^^",exp," <= "~T.stringof~".max"); break; } assert(r == base^^exp, "calc wrong: ",base,"^^",exp," != ",r); } } } test!byte; test!ubyte; test!short; test!ushort; test!int; test!uint; test!long; test!ulong; } ```
Jan 02 2023
On Monday, 4 July 2022 at 08:24:01 UTC, user1234 wrote:Example: ```d auto genDecimalRanks() { import std.conv; auto r = 1; auto result = "["; foreach (i; 1 .. 11) { result ~= to!string(r); if (i < 10) result ~= ", "; r *= 10; } result ~= "]"; return result; } /// like [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] immutable decimalRanks = mixin(genDecimalRanks); ``` It takes much more space and time to write DDOC + the generator than the space + time required to write the equivalent explicit declaration without comments, i.e "self documenting". So metaprog is not the panacea, do you think to that before "meta-progrogramming", or do you "meta-prog" just because it's nice ?If you don't like the iota version then you can just do this: (Modified version of yours.) ```d auto genDecimalRanks() { import std.conv; auto r = 1; int[] result = []; foreach (i; 1 .. 11) { result ~= r; r *= 10; } return result; } /// like [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] static immutable decimalRanks = genDecimalRanks(); ``` Everything at compile-time doesn't have to be strings and mixin :) static and enum will trigger ctfe.
Jul 04 2022
On Monday, 4 July 2022 at 10:29:24 UTC, bauss wrote:On Monday, 4 July 2022 at 08:24:01 UTC, user1234 wrote:still the same problems as the initial version (even if yes I know it's absurd code)[...]If you don't like the iota version then you can just do this: (Modified version of yours.) ```d auto genDecimalRanks() { import std.conv; auto r = 1; int[] result = []; foreach (i; 1 .. 11) { result ~= r; r *= 10; } return result; } /// like [1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000] static immutable decimalRanks = genDecimalRanks(); ``` Everything at compile-time doesn't have to be strings and mixin :) static and enum will trigger ctfe.
Jul 04 2022
On Monday, 4 July 2022 at 10:37:14 UTC, user1234 wrote:still the same problems as the initial version (even if yes I know it's absurd code)I'm actually not sure what you're even trying to achieve or what problem you're trying to solve tbh. Could you perhaps give an example of what your expectations are and how exactly this differs from your expectations of runtime? Because my modified version of yours works both at ctfe and runtime, so it has nothing to do with ctfe really. The only thing ctfe about is that it's statically initialized, but you can use it for runtime arrays too, where the function will be executed at runtime. So there's really no metaprogramming involved in that.
Jul 04 2022
I personally only use metaprogramming for tasks i will know will become repetitive For your example it is not worth it, you only would write that code once anyways For my case here, it perfectly worth it, adding new packet handler is a matter of an attribute The complexity of the code added should be relative to its benefits, no need to over do it ![screenshot](https://i.imgur.com/gLvENTY.png)
Jul 04 2022