digitalmars.D.learn - Is it possible to use templates to implement something like the
- WraithGlade (100/100) Sep 08 Hello everyone! This is my first post in these forums.
- monkyyy (8/12) Sep 08 ```d
- Paul Backus (3/16) Sep 08 This works for `show!"1+2"`, but it won't work for `show!"x"`,
- Paul Backus (15/27) Sep 08 The only way to write a macro in D that executes code in the
- monkyyy (5/15) Sep 08 idk why you calling it a macro, im pretty sure d isnt going to
- Adam D. Ruppe (35/44) Sep 09 Try this:
- WraithGlade (32/32) Sep 09 Thank you all for your very helpful replies and for taking the
- monkyyy (11/12) Sep 09 import std;
- Salih Dincer (37/47) Sep 09 This solution is really successful. I didn't think there could be
- monkyyy (2/3) Sep 09 i isn't being modified by a `*=`. its only the two ++
- WraithGlade (36/49) Sep 09 I tested this version on my machine and the output isn't correct.
- monkyyy (8/14) Sep 09 I made it on dlang.io which does space tabs, if its not ovisous
- Salih Dincer (8/10) Sep 09 After all, you will need it in 2 ways: 1. to learn, 2. to test.
- WB (20/23) Sep 11 Yes.
- Salih Dincer (11/16) Sep 11 ```d
- WB (22/29) Sep 11 Honestly, D, does not really need it, and most of solutions (like
- monkyyy (3/6) Sep 11 this isnt autodecoding, cant be, your oping in debuging code and
- Salih Dincer (11/23) Sep 12 Yes, I agree; what I see is a very complicated Phobos2. Because
Hello everyone! This is my first post in these forums. D is an interesting language (basically a saner C++, which is a nice prospect because I've used C++ working in the game industry in the past) and I've been considering using it and may still do so. However, there is a very basic debugging feature that I've always thought all programming languages should have (because it is so useful and yet also trivial to implement and inherently harmless) but which a surprisingly large number of languages don't support: Specifically, I want a way to create a print command that behaves like ` show` from Julia lang or `dump` from (if my memory is correct) Nim. Basically, I want there to be a way to print both an expression and its value but to only have to write the expression once (which also aids refactoring). Such a feature is extremely useful for faster print-based debugging. Thus, instead of having to write something like this: ``` writeln("1 + 2 == ", 1 + 2); ``` I want to just be able to write this: ``` show!(1 + 2) ``` ... and here is the closest I've been able to get in D, after roughly a dozen attempts: ``` import std.stdio; void show(alias expr)() { writeln(expr.stringof, " == ", expr); //https://ddili.org/ders/d.en/templates_more.html //says that `alias` allows "any expression" but in //reality the expression gets evaluated and hence //information about what it really was is lost. } void main() { show!(1 + 2); //I want this to print "1 + 2 == 3", //but instead it prints "3 == 3" const x = 1 + 2; show!(x); //This at least prints "x == 3" though. } ``` See the comments in the above code for info on what it does versus what I want it to do. Usually I prefer `assert` whenever I actually know what the values of something are, but there are many cases where I don't know what the values are and want to see easily. **This feature is a great litmus test for how expressive a language actually is.** Indeed, it is one of the best simple litmus tests for programming language design that I've found. It is trivial yet speaks volumes of the thought that has gone into the design of any language. Even $5 solar-powered desktop calculators often leave the expression of what you write *still visible* after you've computed it, yet many programming languages don't even provide a way of eliminating this extremely common form of redundancy and require duplicated typing in strings repeatedly. One would think that more programming languages would account for this when you consider that much of the *whole purpose* of programming languages is enabling the user to get computed values evaluated and displayed as easily and pragmatically as possible... yet so many languages can't even do the above despite it being one of the most obvious ergonomic factors one could ever have for anything that is used for computation. That is why the absence of it in any language annoys me so much. The lack of it is an absurdity, essentially, if you think about it from first principles and user experience. On the other hand, I am aware that macros make control flow harder to follow, but to counter that: String-generating macros that aid debugging are an inherently harmless subset of macros. They only make it easier to debug and display diagnostics. There's nothing about the `show` macro (or similar passive-information constructs) conceptually that harms comprehensibility of code. I've for years thought it would be nice to have a language that lacks the more extreme macros but still has the ability to implement things like the `show` macro. Is D such a language? Is there any way to implement what I want (as above)? The frustration with trying to get this working makes me consider just using Nim instead, which has extremely flexible macros and can do this easily. I personally believe that all languages should have this, even if they have to make a one-time exception to allow just the `show` macro but not other macros. Why should merely displaying the correspondence between expressions and their outputs be hard or impossible in so many languages? Think about it. It is bizarre that it is so often absent. If D can't do this currently then I suggest that it be made to be capable of it. This kind of macro is not like other macros. Debugging and diagnostic pragmatism doesn't confound program logic, unlike how esoteric macros often do. Anyway, thank you for reading and sorry for my verbosity. This is one of my biggest pet peeves regarding programming language design.
Sep 08
On Sunday, 8 September 2024 at 22:01:10 UTC, WraithGlade wrote:I want to just be able to write this: ``` show!(1 + 2) ``````d void show(string s)(){ auto res=mixin(s); writeln(s,"==",res); } show!"1+2"; ```
Sep 08
On Sunday, 8 September 2024 at 23:01:22 UTC, monkyyy wrote:On Sunday, 8 September 2024 at 22:01:10 UTC, WraithGlade wrote:This works for `show!"1+2"`, but it won't work for `show!"x"`, because `x` is not visible inside the body of the `show` function.I want to just be able to write this: ``` show!(1 + 2) ``````d void show(string s)(){ auto res=mixin(s); writeln(s,"==",res); } show!"1+2"; ```
Sep 08
On Sunday, 8 September 2024 at 22:01:10 UTC, WraithGlade wrote:Basically, I want there to be a way to print both an expression and its value but to only have to write the expression once (which also aids refactoring). Such a feature is extremely useful for faster print-based debugging. Thus, instead of having to write something like this: ``` writeln("1 + 2 == ", 1 + 2); ``` I want to just be able to write this: ``` show!(1 + 2) ```The only way to write a macro in D that executes code in the scope where the macro is *invoked* (rather than the scope where it's *defined*) is to use a `mixin`: ```d enum show(string expr) = `writeln(q"(` ~ expr ~ `)", " == ", ` ~ expr ~ `);`; void main() { import std.stdio; mixin(show!"1 + 2"); // 1 + 2 == 3 const int x = 1 + 2; mixin(show!"x"); // x == 3 } ```
Sep 08
On Monday, 9 September 2024 at 00:34:03 UTC, Paul Backus wrote:The only wayis that a challenge?void main() { import std.stdio; mixin(show!"1 + 2"); // 1 + 2 == 3 const int x = 1 + 2; mixin(show!"x"); // x == 3 } ```idk why you calling it a macro, im pretty sure d isnt going to make it easy to climb up the ast ever, and op wants sometin that reads statement literals.
Sep 08
On Sunday, 8 September 2024 at 22:01:10 UTC, WraithGlade wrote:Basically, I want there to be a way to print both an expression and its value but to only have to write the expression once (which also aids refactoring). Such a feature is extremely useful for faster print-based debugging. [...] I want to just be able to write this: ``` show!(1 + 2) ```Try this: ``` import core.interpolation; void show(Args...)(InterpolationHeader hdr, Args args, InterpolationFooter ftr) { import std.stdio; foreach(arg; args) { static if(is(typeof(arg) == InterpolatedExpression!code, string code)) write(code); else static if(is(typeof(arg) == InterpolatedLiteral!str, string str)) write(str); else write(" = ", arg); } writeln(); } ``` Used like: ``` void main() { int a = 5, b = 22; show(i`$(a), $(b), $(a + b)`); } ``` Output: a = 5, b = 22, a + b = 27 You can show as many expressions as you want in there, each $(....) thing is expanded to `code inside = result`. So show(i"$(1 + 2)"); // prints 1 + 2 = 3 The interpolated sequence syntax makes it a bit heavier: that `i"$( .... )"` around it is required here, but it works anyway.
Sep 09
Thank you all for your very helpful replies and for taking the time to make them! It is indeed heartening to see that D supports multiple different forms of this mechanism. This is certainly sufficient for the example I gave. I suggest that something like this should be added to the standard library and perhaps even shown in early examples in the tutorial and documentation. Such things are so routine that I would argue that it should be the most common form of print-based debugging. It also seems to be becoming an expected/standard feature among new languages too, for obvious practical reasons. The prospect of using D does look brighter now, knowing all this. ---- More specifically, I am planning to do a combination of general application development, game development, and eventually building a language transpiler of my own (for which I've been gradually coming up with ideas for over the years). I have grown tired of dealing with C and C++'s mess and build ecosystem. D's expressiveness is adequate now that I know what you've all said here. I am debating between D, Nim, Zig, and Rust. Each have pros and cons, although all are clearly great and none would be "bad" choices (barring unexpected events). I will consider it for a while longer. I want a stable platform to build on long-term that is also flexible and pleasant to work with. So, tangentially, I'd also be interested in hearing any of your all's thoughts about that regarding D (and/or the other prospective languages). Thanks again for reading and for the fast and extremely helpful replies! It is much appreciated! I wish you all a wonderful day/night/week/etc!
Sep 09
On Monday, 9 September 2024 at 17:39:46 UTC, WraithGlade wrote:import std; auto parse(char[] s)=>s[9..$-2]; void show(T,string file= __FILE__,int line=__LINE__)(T t){ writeln(File(file).byLine.drop(line-1).front.parse," == ",t); } void main(){ int i=3; show(i++ + ++i * i); show(i); }
Sep 09
On Monday, 9 September 2024 at 17:56:04 UTC, monkyyy wrote:auto parse(char[] s)=>s[9..$-2]; void show(T,string file= __FILE__,int line=__LINE__)(T t){ writeln(File(file).byLine.drop(line-1).front.parse," == ",t); } void main(){ int i=3; show(i++ + ++i * i); show(i); }This solution is really successful. I didn't think there could be any other solution than mixin(); this is utterly ingenious! There is a working version of the code below and different tests. My only concern is why i == 5 in the line below when i == 28? ```d import std; auto parse(char[] s) => s[9..$ - 2]; void show(string file = __FILE__, int line = __LINE__, T)(T t) { File(file).byLine .drop(line-1) .front .parse .writeln(" == ", t); } template f(ulong n) { static if(n < 2) const f = 1; else const f = n * f!(n - 1); } void main() { int i = 3; show(i++ + ++i * i); show(i); T factorial(T)(T n)=>n<2?1:n*factorial(--n); show(factorial(10)); show(f!10); } /* Prints: i++ + ++i * i == 28 i == 5 factorial(10) == 3628800 f!10 == 3628800 */ ``` SDB 79
Sep 09
On Monday, 9 September 2024 at 19:29:01 UTC, Salih Dincer wrote:My only concern is why i == 5 in the line below when i == 28?i isn't being modified by a `*=`. its only the two ++
Sep 09
On Monday, 9 September 2024 at 17:56:04 UTC, monkyyy wrote:On Monday, 9 September 2024 at 17:39:46 UTC, WraithGlade wrote:I tested this version on my machine and the output isn't correct. It gets cut off. Here's the reformatted and slightly altered version I tested: ``` import std; auto parse(char[] s) => s[9 .. $ - 2]; void show(T, string file = __FILE__, int line = __LINE__)(T t) { File(file) .byLine .drop(line - 1) .front .parse .writeln(" == ", t); } void main(){ int i = 3; show(i++ + ++i * i); show(i); } ``` It outputs this erroneous result: ``` + + ++i * i) == 28 == 5 ``` This is not that surprising considering it uses hardcoded numbers and doesn't look right. Also, moving the built EXE out of the source code directory and into a different folder causes the program to crash when it runs due to not being able to find the source code file. That seems to indicate that this code's file reading doesn't actually happen in compile-time. Maybe there are compiler build options for D that'll write this in in compile time though? In any case, this version seems more brittle than the others at leastimport std; auto parse(char[] s)=>s[9..$-2]; void show(T,string file= __FILE__,int line=__LINE__)(T t){ writeln(File(file).byLine.drop(line-1).front.parse," == ",t); } void main(){ int i=3; show(i++ + ++i * i); show(i); }
Sep 09
On Monday, 9 September 2024 at 20:52:09 UTC, WraithGlade wrote:This is not that surprising considering it uses hardcoded numbers and doesn't look right.I made it on dlang.io which does space tabs, if its not ovisous to a beginner, parse is ~~lazy~~ not correct, it need to do find substring nonsense for real codeThat seems to indicate that this code's file reading doesn't actually happen in compile-time. Maybe there are compiler build options for D that'll write this in in compile time though?This is one of the old fights, you will have a bad time "ctfe io isnt safe", if your making your own build system `-J=.` and string imports are an option but that will not be aviable for generic code; meaning you cant make something nice for the std.
Sep 09
On Monday, 9 September 2024 at 20:52:09 UTC, WraithGlade wrote:In any case, this version seems more brittle than the others at leastAfter all, you will need it in 2 ways: 1. to learn, 2. to test. After testing, you can disable it with just the version parameter while compiling. It is even possible to turn it on/off by embedding ```// version = logOn; in the code and removing the comment feature. For this, embed VERSION(logOn) in the relevant lines... SDB 79
Sep 09
On Sunday, 8 September 2024 at 22:01:10 UTC, WraithGlade wrote:Specifically, I want a way to create a print command that behaves like ` show` from Julia lang or `dump` from (if my memory is correct) Nim.Yes. https://github.com/baryluk/echo/blob/master/echo.d#L205-L209 ```d mixin(echo("echo test: i=$i j=$j Escaping: \\$j, Complicated i+j=${i+j}, End of tests.")); ``` There are other ways of doing similar (using mixing templates for example), but the machinery would be very similar. This code is about 13 years old, but still works. (It is functional and works, but I never it used more than what is in this repo). But now that we have interpolation sequences in the language, it would be way easier, cleaner and more powerful to use them. I have my custom Linux IO library, that utilizes this, and you can do thing like this: ```d Println(i"Ala ma $(ShortHex(123+foo*bar)) $(&m3) $(y.ptr) maybe"); ``` (with zero memory allocations or excessive memory copies).
Sep 11
On Wednesday, 11 September 2024 at 18:29:39 UTC, WB wrote:This code is about 13 years old, but still works. (It is functional and works, but I never it used more than what is in this repo). But now that we have interpolation sequences in the language, it would be way easier, cleaner and more powerful to use them.```d assert(echo2("T $i, b, ${i-2}") == "writefln(\"T \", i, \", b, \", (i-2));\n"); ``` It looks clean and understandable. What is not understandable is that it has not been included in std.stdio for over 10 years. I know, string interpolation has just been integrated, but why hasn't something like this been developed in 10 years that would have no side effects and would attract the attention of beginners? SDB 79
Sep 11
On Wednesday, 11 September 2024 at 19:44:54 UTC, Salih Dincer wrote:It looks clean and understandable. What is not understandable is that it has not been included in std.stdio for over 10 years. I know, string interpolation has just been integrated, but why hasn't something like this been developed in 10 years that would have no side effects and would attract the attention of beginners? SDB 79Honestly, D, does not really need it, and most of solutions (like above) do have one or few limitations and drawbacks. Some will be acceptable in some projects, some not. There is plenty of stuff in language and library already, adding small things like that are not really the best thing. There is plenty of very successful languages, that do not offer this (Go, C++, Python 3.5 and earlier, Lua, older JavaScript, etc) and just adding it will not make D automatically more successful. I feel there is too much already in D and standard library, and things are added to quickly and eagerly, and years later we end up in a mess that cannot be solved (because of compatibility). Interpolation strings (added this year) are ok and pretty decent, and well tough in terms of what one can do with them. We still do not know if it is a good or bad design, but we will see. Interpolation string are way more powerful, and cleaner, so it is good half solution like my mixin echo code was not used as a general solution. If you want to use something, just put it in your own project, and it will work to your liking. It does not need to be in a standard library.
Sep 11
On Wednesday, 11 September 2024 at 22:06:54 UTC, WB wrote:I feel there is too much already in D and standard library, and things are added to quickly and eagerly, and years later we end up in a mess that cannot be solved (because of compatibility).this isnt autodecoding, cant be, your oping in debuging code and is for temp stuff
Sep 11
On Wednesday, 11 September 2024 at 22:06:54 UTC, WB wrote:Honestly, D, does not really need it, and most of solutions (like above) do have one or few limitations and drawbacks. Some will be acceptable in some projects, some not. There is plenty of stuff in language and library already, adding small things like that are not really the best thing. There is plenty of very successful languages, that do not offer this (Go, C++, Python 3.5 and earlier, Lua, older JavaScript, etc) and just adding it will not make D automatically more successful. I feel there is too much already in D and standard library, and things are added to quickly and eagerly, and years later we end up in a mess that cannot be solved (because of compatibility).Yes, I agree; what I see is a very complicated Phobos2. Because it has become like this over time and submodules have been developed to eliminate the confusion. For example, there are things in std.math that we do not use often, a very basic algorithm like gcd() is expected to be written in std.numeric or the factorial() function by you. In short, Phobos3 will adapt better to the language for beginners if it is written systematically from the beginning; std.math should be reorganized from scratch. SDB 79
Sep 12