Thread overview
[Issue 23916] Non-eponymous template instances have a "type" (void)
May 12, 2023
Nick Treleaven
May 12, 2023
Vladimir Panteleev
May 13, 2023
Nick Treleaven
May 13, 2023
Vladimir Panteleev
May 13, 2023
Nick Treleaven
May 13, 2023
Vladimir Panteleev
May 13, 2023
Nick Treleaven
May 13, 2023
Vladimir Panteleev
May 13, 2023
Vladimir Panteleev
May 12, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

Nick Treleaven <nick@geany.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |nick@geany.org

--- Comment #1 from Nick Treleaven <nick@geany.org> ---
template X() {}

It was decided that typeof(X) is void, that's in the spec. I'm not sure if the type of a template instance is defined in the spec. A template instance seems similar to a module. However `typeof(std.math)` is a compile error.

void fun()
{
    return X!();
}

Note: You are allowed to return an expression of type void, this is useful in generic code.

--
May 12, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #2 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to Nick Treleaven from comment #1)
> template X() {}
> 
> It was decided that typeof(X) is void, that's in the spec. I'm not sure if the type of a template instance is defined in the spec.

Any reason not to revise and deprecate this?

> Note: You are allowed to return an expression of type void, this is useful in generic code.

Yes. Although, it is an accidental, half-finished feature. You cannot have void variables or function parameters. Ironically however, you can have noreturn variables and function parameters.

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #3 from Nick Treleaven <nick@geany.org> ---
> Any reason not to revise and deprecate this?

It would break code like this:

import std.stdio;

template t()
{
        int i;
}

void f(alias a)() if (is(typeof(a)))
{
        writeln(a.i);
}

void main()
{
        alias a = t!();
        f!a();
}

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #4 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to Nick Treleaven from comment #3)
> void f(alias a)() if (is(typeof(a)))

This looks meaningless to me. Why would you want to write code like this - what is the constraint even supposed to check? Why should we not deprecate then remove taking the type of a template / template instance?

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #5 from Nick Treleaven <nick@geany.org> ---
It checks if `a` has a type. It tells the reader that `a` is not a module or a type itself.

Why is it a problem if a template instance has a type?

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #6 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to Nick Treleaven from comment #5)
> It checks if `a` has a type. It tells the reader that `a` is not a module or a type itself.

Am I missing something? How does it tell that to the reader, unless the reader is aware of this weird quirk in the language? Wouldn't something more explicit be much better?

What is the real use case? Do you have any real-world code which illustrates why this rule in the language is useful?

> Why is it a problem if a template instance has a type?

It allows (accidentally, or maliciously) writing working but confusing code, and allows accidentally writing non-working code which is difficult to diagnose why it's not working.

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #7 from Nick Treleaven <nick@geany.org> ---
Can you give examples of confusing code?

> How does it tell that to the reader, unless the reader is aware of this weird quirk in the language?

First, you should acknowledge that the constraint is not meaningless as you said. The usefulness of that constraint is independent of this issue.

Next, the reader should understand constraints. They don't have to know that a template instance has void type, yet their code will magically work for people who do know that.

> Wouldn't something more explicit be much better?

Such as what?

> What is the real use case?

You are asking for a change so the burden is on you to justify it. If you like, make a draft pull request and see if it breaks anything.

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #8 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to Nick Treleaven from comment #7)
> Can you give examples of confusing code?

I find your examples above confusing.

It is meaningless for a template to have a type.

It is also meaningless for a template instance to have a type, unless of course it is an eponymous template which resolves to something that has a type.

"void" is an arbitrary choice. Why the unit type? Why not the bottom type? Why not typeof(null), etc.?

> First, you should acknowledge that the constraint is not meaningless as you said. The usefulness of that constraint is independent of this issue.
> 
> Next, the reader should understand constraints. They don't have to know that a template instance has void type, yet their code will magically work for people who do know that.

I have no idea what you're trying to say by this.

> > Wouldn't something more explicit be much better?
> 
> Such as what?

To check that something is a type: if (is(X))
To check that something is a template: if (__traits(isTemplate, X))
To check that something is a template instance: if (is(X : Template!Args, alias
Template, Args...)) (or use alias parameter destructuring)
To check that something is an instance of a particular template: if
(__traits(isSame, TemplateOf!X, SomeTemplate))

Yeah, these are also awkward and inconsistent. std.traits does have a bunch of helpers, such as `isType`, and there've been proposals to put something more readable on top of IsExpression in Phobos.

> You are asking for a change so the burden is on you to justify it. If you like, make a draft pull request and see if it breaks anything.

What is this, 90's newsgroups? I'm not playing this "burden of proof" BS.

There is nothing personal about this issue. We do not file requests that we are personally "asking for"; we file proposals on how to progress on our shared goal of making the language better. And bugs.

Every single bit of the language spec needs to be be defensible. If something is not backed by a strong rationale, it is candidate for revision, and we do need to continuously attack and revise the weak parts of the language. Otherwise, we are never going to get a better language.

And yeah, fixing this will probably require a deprecation cycle.

--
May 13, 2023
https://issues.dlang.org/show_bug.cgi?id=23916

--- Comment #9 from Vladimir Panteleev <dlang-bugzilla@thecybershadow.net> ---
(In reply to Nick Treleaven from comment #7)
> > Wouldn't something more explicit be much better?
> 
> Such as what?

For the specific example above, probably this:

void f(alias a)() if (is(typeof(a.i) : int))

or:

void f(alias a)() if (is(typeof(writeln(a.i))))

depending on your intention.

--