Jump to page: 1 2
Thread overview
Is it possible to use templates to implement something like the `@show` macro from the Julia programming langauge in D?
Sep 08
monkyyy
Sep 09
monkyyy
Sep 09
monkyyy
Sep 09
monkyyy
Sep 09
monkyyy
Sep 11
WB
Sep 11
WB
Sep 11
monkyyy
September 08

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.

September 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)
void show(string s)(){
  auto res=mixin(s);
  writeln(s,"==",res);
}

show!"1+2";
September 09

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:

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
}
September 09

On Sunday, 8 September 2024 at 23:01:22 UTC, monkyyy wrote:

>

On Sunday, 8 September 2024 at 22:01:10 UTC, WraithGlade wrote:

>

I want to just be able to write this:

show!(1 + 2)
void show(string s)(){
  auto res=mixin(s);
  writeln(s,"==",res);
}

show!"1+2";

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.

September 09

On Monday, 9 September 2024 at 00:34:03 UTC, Paul Backus wrote:

>

The only way

is 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.

September 09

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.

September 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!

September 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);
}

September 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?

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

September 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 ++

« First   ‹ Prev
1 2