August 22, 2022

On Monday, 22 August 2022 at 14:43:24 UTC, Andrey Zherikov wrote:

>

But the question is still opened: why is typeof(U().func!0) not the same as typeof(U().func!0())?

Probably because if it were the same, it would be completely impossible to introspect on the type of U.func!0 directly. The closest you could get would be to examine typeof(&U.func!0); i.e., the type of the function pointer rather than the function itself.

I'm not totally convinced that the current behavior is the correct decision here, but there is a real tradeoff.

August 22, 2022

On Monday, 22 August 2022 at 15:15:22 UTC, Paul Backus wrote:

>

My first instinct is to say that this is the user's mistake. UDAs are not evaluated like normal expressions, and anyone using UDAs is going to have to learn that sooner or later.

My feeling was that UDA expression is just an expression that's evaluated at compile time. Am I wrong? Where can I read about the differences between UDA and 'normal' expressions?

>

That said, if you want to try and present a nicer API, my recommendation would be to accept either (a) an instance of U, or (b) a callable that returns a U, which you can do with code like the following:

import std.traits, std.meta;

enum isOrReturnsU(alias attr) = is(typeof(attr) == U) || is(typeof(attr()) == U);
alias getMyUDAs(alias sym) = Filter!(isOrReturnsU, getUDAs!sym);

Or if you want to generalize to arbitrary predicates:

alias filterUDAs(alias sym, alias pred) = Filter!(pred, getUDAs!sym);

Thanks for this suggestion, I'll try it out!

August 22, 2022

On Monday, 22 August 2022 at 15:20:46 UTC, Paul Backus wrote:

>

On Monday, 22 August 2022 at 14:43:24 UTC, Andrey Zherikov wrote:

>

But the question is still opened: why is typeof(U().func!0) not the same as typeof(U().func!0())?

Probably because if it were the same, it would be completely impossible to introspect on the type of U.func!0 directly. The closest you could get would be to examine typeof(&U.func!0); i.e., the type of the function pointer rather than the function itself.

I'm not totally convinced that the current behavior is the correct decision here, but there is a real tradeoff.

I have an impression that template function can be called without parenthesis if it doesn't have run-time parameters so func!0 is the same as func!0(). Am I wrong?

If we consider free function vs. member function, why is typeof(u.func!0) not the same as typeof(u.func2!0) here?

struct U
{
    ref U func(int i)() { return this; }
}
ref U func2(int i)(ref U u) { return u; }

void main()
{
    U u;
    pragma(msg, typeof(u.func!0));	// pure nothrow @nogc ref @safe U() return
    pragma(msg, typeof(u.func2!0));	// U
}

Is typeof(u.func2!0) correct in this case? If so then I'll just convert my API to free-standing functions and everything will work as a magic (without my own implementation of getUDA).

August 22, 2022

On Monday, 22 August 2022 at 16:06:37 UTC, Andrey Zherikov wrote:

>

On Monday, 22 August 2022 at 15:15:22 UTC, Paul Backus wrote:

>

My first instinct is to say that this is the user's mistake. UDAs are not evaluated like normal expressions, and anyone using UDAs is going to have to learn that sooner or later.

My feeling was that UDA expression is just an expression that's evaluated at compile time. Am I wrong? Where can I read about the differences between UDA and 'normal' expressions?

A UDA can be either an expression or a symbol. Here are some examples of symbol UDAs that are not valid expressions:

import std.stdio;
struct S;
template t() {}

@S           // a type
@(std.stdio) // a module
@t           // a template
int n;
August 22, 2022

On 8/22/22 12:19 PM, Andrey Zherikov wrote:

>

On Monday, 22 August 2022 at 15:20:46 UTC, Paul Backus wrote:

>

On Monday, 22 August 2022 at 14:43:24 UTC, Andrey Zherikov wrote:

>

But the question is still opened: why is typeof(U().func!0) not the same as typeof(U().func!0())?

Probably because if it were the same, it would be completely impossible to introspect on the type of U.func!0 directly. The closest you could get would be to examine typeof(&U.func!0); i.e., the type of the function pointer rather than the function itself.

I'm not totally convinced that the current behavior is the correct decision here, but there is a real tradeoff.

I have an impression that template function can be called without parenthesis if it doesn't have run-time parameters so func!0 is the same as func!0(). Am I wrong?

If we consider free function vs. member function, why is typeof(u.func!0) not the same as typeof(u.func2!0) here?

struct U
{
     ref U func(int i)() { return this; }
}
ref U func2(int i)(ref U u) { return u; }

void main()
{
     U u;
     pragma(msg, typeof(u.func!0));    // pure nothrow @nogc ref @safe U() return
     pragma(msg, typeof(u.func2!0));    // U
}

Is typeof(u.func2!0) correct in this case? If so then I'll just convert my API to free-standing functions and everything will work as a magic (without my own implementation of getUDA).

It's not magic. It's UFCS.

in the case of the struct function, there is an ambiguity. Do you mean the function named func in the namespace of u (which is actually U)? Or do you mean to call func using u? The D compiler opts for the former.

However, with func2, there is no func2 in u's namespace. So the only option is an actual call.

-Steve

August 22, 2022

On Monday, 22 August 2022 at 16:19:06 UTC, Andrey Zherikov wrote:

>

I have an impression that template function can be called without parenthesis if it doesn't have run-time parameters so func!0 is the same as func!0(). Am I wrong?

You're not wrong. This behavior is a special case in typeof. I was merely attempting to explain why such a special case might have been introduced.

>

If we consider free function vs. member function, why is typeof(u.func!0) not the same as typeof(u.func2!0) here?

My guess is that u.func2!0 is rewritten to func2!0(u) during semantic analysis, so it does not trigger the special-case behavior.

>

Is typeof(u.func2!0) correct in this case? If so then I'll just convert my API to free-standing functions and everything will work as a magic (without my own implementation of getUDA).

It's probably not worth completely changing your API design just to work around this issue. Also, even if you do this, it is still possible for a user to run into a same problem with a member function of one of their own types; for example:

import your.library;

struct MyStruct {
    U myFunc() { /* ... */ }
}

@(MyStruct.myFunc) whatever;
August 22, 2022

On Monday, 22 August 2022 at 16:42:50 UTC, Paul Backus wrote:

>

It's probably not worth completely changing your API design just to work around this issue. Also, even if you do this, it is still possible for a user to run into a same problem with a member function of one of their own types; for example:

import your.library;

struct MyStruct {
    U myFunc() { /* ... */ }
}

@(MyStruct.myFunc) whatever;

This will be users' issue then, not mine :)

I'm providing a struct (U in examples above) with some API and I'm looking for this struct as an UDA (through hasUDA/getUDAs). And actually I already have a mix of member-functions and free-functions in my API so it's gonna be more consistent if I make all of them free-functions.

August 22, 2022

On Monday, 22 August 2022 at 17:40:59 UTC, Andrey Zherikov wrote:

>

I'm providing a struct (U in examples above) with some API and I'm looking for this struct as an UDA (through hasUDA/getUDAs). And actually I already have a mix of member-functions and free-functions in my API so it's gonna be more consistent if I make all of them free-functions.

Just checked - free functions work so I'll migrate API to them.

Thanks everyone for answering my questions.

1 2
Next ›   Last »