www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Make function pointers and delegates consistent

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
Underlying secondary theme: Make function pointer types not be 
pointer types.

Function pointers are, at least in my mental model, the same as a 
delegate, just with no context. This has a few immediate 
downstream effects, e.g. function pointers can’t have 
member-function-only attributes.

But that’s not how the D specifies them. There are a lot of 
asymmetries between function pointers and delegates, which I 
think a new Edition could change for the better.

This proposal would push function types (not: function 
**pointer** types) into a much smaller niche.

I can’t list them all off the top of my head. A few are:

1. An `is(T == function)` checks if `T` is a function type 
despite `function` denoting function pointers, but `is(T == 
delegate)` works as expected.\
In other words, `is(typeof(function() {}) == function)` fails, 
but `is(typeof(delegate() {}) == delegate)` passes, and that 
makes no sense whatsoever.

2. Function pointers can bind to `T*` (inferring `T` as a 
function type), delegates obviously can’t.\
Syntactically speaking, that makes no sense. `T*` should only 
bind to types that are *syntactically* `T*`. The fact that they 
bind function pointers because they’re pointers internally 
doesn’t fly: `T*` doesn’t bind class objects, but those are 
internally pointers as well. I can guess the reason for that: D 
has function types, but no “underlying class” type. D would be a 
better language if it pretended function pointer types were not 
in fact a pointer to something.

3. A function pointer `fp` can be called with `fp(…)` and 
`(*fp)(…)`, a delegate `dg` can be called using `dg(…)`, but not 
`(*dg)(…)`. \
There is no good reason to write `(*fp)(…)`. It’s an unwieldy C 
relic, and we don’t really need to encourage it, maybe even 
remove it in a future Edition. ImportC largely reduced the need 
for D to be source-compatible with C.

4. On lambdas, `typeof` is a function pointer or a delegate; on a 
defined function, it’s a function type. Just make it consistent 
and let `typeof` return a function pointer or delegate type. \
“But can’t you just write `typeof(&f)` when `f` is a function?” 
Yes, but what if `f` isn’t a specific function I know, but a 
template alias parameter? Then, `typeof(f)` gives me a function 
type when a defined functions is passed, and a function pointer 
or delegate type if a lambda is passed. In most cases, templates 
don’t care which of these it is, but if I want to inspect the 
thing, I have to do needless case distinctions, also because of 
issues above.

---

The bottom line is: Function types are weird and don’t interact 
well with most parts of the language from parsing to semantics, 
but they seep through cracks and burden the programmer with 
irrelevant details whenever they show up. Let’s make them show up 
less.

First of all, we should decide if we even want to have them in 
the language officially. Their status seems to be: 
Un(der)documented DMD feature. That’s bad. Either remove them 
entirely from the language (not the compiler internals) or 
promote them to be more useful: For example, a production rule 
for them in the type grammar would make spelling them more 
accessible (also in diagnostics). It can already be spelled as 
`typeof(*int function(int)  safe.init)`, but as I pointed out, 
you shouldn’t dereference function pointers, so instead, add a 
`__traits` or whatever.
Nov 20 2025
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 20 November 2025 at 12:56:55 UTC, Quirin Schroll 
wrote:
 4. On lambdas, `typeof` is a function pointer or a delegate; on 
 a defined function, it’s a function type. Just make it 
 consistent and let `typeof` return a function pointer or 
 delegate type.
The *most* consistent thing to do here would be to have `typeof(f)` treat `f` as a function-call expression with parentheses omitted (so, equivalent to `typeof(f())`), and force the user to write `typeof(&f)` to get the function-pointer type. This would remove the special case where `typeof` sometimes treats its argument as a symbol and sometimes as an expression, and allow programmers to remove workarounds for this special case from generic code. It would also make the ` property` attribute completely redundant and easy to remove, if we want to do that in the future.
Nov 20 2025
parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 20 November 2025 at 14:13:02 UTC, Paul Backus wrote:
 On Thursday, 20 November 2025 at 12:56:55 UTC, Quirin Schroll 
 wrote:
 4. On lambdas, `typeof` is a function pointer or a delegate; 
 on a defined function, it’s a function type. Just make it 
 consistent and let `typeof` return a function pointer or 
 delegate type.
The *most* consistent thing to do here would be to have `typeof(f)` treat `f` as a function-call expression with parentheses omitted (so, equivalent to `typeof(f())`), and force the user to write `typeof(&f)` to get the function-pointer type. This would remove the special case where `typeof` sometimes treats its argument as a symbol and sometimes as an expression, and allow programmers to remove workarounds for this special case from generic code. It would also make the ` property` attribute completely redundant and easy to remove, if we want to do that in the future.
I’ll give you that implicit invocation is an oddity, but that’s a problem with ` property`’s design. The ` property` annotation aside, `&` and `typeof` both don’t implicitly invoke the outermost symbol. That is consistent. I already explained how this solution is annoying and cumbersome. If you have an alias `f`, then currently, `typeof(&f)` is a function pointer if `f` is a function and would be a pointer to function pointer or pointer to delegate if `f` is a lambda. The right way to do it is to make `typeof` never return a function type and always “promote” those to function pointers.
Nov 20 2025
parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 20 November 2025 at 14:46:45 UTC, Quirin Schroll 
wrote:
 I already explained how this solution is annoying and 
 cumbersome. If you have an alias `f`, then currently, 
 `typeof(&f)` is a function pointer if `f` is a function and 
 would be a pointer to function pointer or pointer to delegate 
 if `f` is a lambda.
It sounds to me like this is an inconsistency with `alias`, not an inconsistency with `typeof`. I don't think we should be adding more special cases to `typeof` to compensate for inconsistencies elsewhere in the language.
Nov 20 2025
prev sibling parent Nick Treleaven <nick geany.org> writes:
On Thursday, 20 November 2025 at 12:56:55 UTC, Quirin Schroll 
wrote:
 1. An `is(T == function)` checks if `T` is a function type 
 despite `function` denoting function pointers, but `is(T == 
 delegate)` works as expected.\
 In other words, `is(typeof(function() {}) == function)` fails, 
 but `is(typeof(delegate() {}) == delegate)` passes, and that 
 makes no sense whatsoever.
In June I added an example showing the semantics to the *IsExpression* spec. Yes this is confusing and somewhat bug-prone. It also confused someone writing docs for `std.traits.isFunction` which didn't get fixed from 2016 until now: https://github.com/dlang/phobos/pull/10904 This could be remedied by having `__traits(isFunction, X)` and deprecating/discouraging use of `is(X == function)`. ...
 The bottom line is: Function types are weird and don’t interact 
 well with most parts of the language from parsing to semantics, 
 but they seep through cracks and burden the programmer with 
 irrelevant details whenever they show up. Let’s make them show 
 up less.
Removing function types is an interesting idea, but I'm not sure that it's worth the (likely?) breakage of existing code that uses the existing type semantics to determine when a symbol is actually a function vs a function pointer. I don't know that this is enough of an issue to do it even in an edition.
 First of all, we should decide if we even want to have them in 
 the language officially. Their status seems to be: 
 Un(der)documented DMD feature.
Any suggestions on how to improve the docs? https://dlang.org/spec/type.html#functions
Nov 22 2025