July 30, 2020
On Wednesday, 29 July 2020 at 16:31:08 UTC, Jean-Louis Leroy wrote:
> On Wednesday, 29 July 2020 at 16:11:02 UTC, Petar Kirov [ZombineDev] wrote:
>> On Wednesday, 29 July 2020 at 12:54:36 UTC, Adam D. Ruppe wrote:
>>> On Wednesday, 29 July 2020 at 10:38:29 UTC, Manu wrote:
>>>> On Wed, Jul 29, 2020 at 9:40 AM Jean-Louis Leroy via
>>>>> Guaranteed failure then? ;-)
>>>>
>>>> Yes.
>>>
>>> What's wrong with my solution earlier in the thread?
>>
>> That it uses a string mixin :P
>>
>> What Manu is arguing is that if parameter storage classes were instead proper type qualifiers, then one could trivially manipulate them with std.meta. And then supposedly there would be no need to string mixins at all.
>
> If we go back to the original problem, you still need a string mixin to inject the function name in two places.
>
> As for Adam's solution, it solves a slightly different problem but I don't see why he uses a string mixin:
>
> template forward(alias fun)
> {
>     import std.traits;
>     @(__traits(getAttributes, fun)) auto ref forward(Parameters!fun args) {
>         return fun(args);
>     }
> }
>
> @(42) void myfun(int, ref double x, out string s);
>
> pragma(msg, typeof(forward!myfun));
> // void function(int _param_0, ref double _param_1, out string _param_2) @system
> pragma(msg, __traits(getAttributes, forward!myfun));
> // tuple(42)
>
> Anyway, storage classes are not a difficulty, as long as you use the whole __parameters, or slice it (__parameters[0..1]), and refrain from indexing it (__parameters[0] loses storage classes). Strange beast...

My point, that you can't do much useful processing with `__parameters` (except pass all of subset of it), still stands.

Say you have a function with N parameters. Some of those parameters are integers and may have a UDA attached to them that specifies the minimum and maximum value they may receive. For example:

R fun(
  scope P1 arg1,
  return int* arg2,
  lazy @intetval(-16, 320) long arg3,
  @interval(0, 127) uint arg4,
  ref @interval(0, 8192) size_t arg5
);

The task is create a template, which given a function type like typeof(&fun) returns a new function type with all integer parameters replaced by the smallest type that is big enough to hold the interval specified by the UDA and preserves all storage classes.


R hun(
  scope P1 arg1,
  return int* arg2,
  lazy @intetval(-16, 320) short arg3,
  @interval(0, 127) ubyte arg4,
  ref @interval(0, 8192) ushort arg5
);

The way I'd like to go about solving this is like this:

template TightenIntegerParams(Fun)
{
  alias TightenIntegerParams =
    ReturnType!Fun function(
      staticMap!(
        TightenParam,
        Parameters!Fun
      )
    );
}

However the moment staticMap internally does `Args[i]` in order to pass the i-th element to the mapping function `F` we lose all information about the storage classes and UDAs.

July 30, 2020
On Thursday, 30 July 2020 at 04:56:55 UTC, Petar Kirov [ZombineDev] wrote:
> On Wednesday, 29 July 2020 at 16:31:08 UTC, Jean-Louis Leroy wrote:
>> [...]
>
> My point, that you can't do much useful processing with `__parameters` (except pass all of subset of it), still stands.
>
> Say you have a function with N parameters. Some of those parameters are integers and may have a UDA attached to them that specifies the minimum and maximum value they may receive. For example:
>
> R fun(
>   scope P1 arg1,
>   return int* arg2,
>   lazy @intetval(-16, 320) long arg3,
>   @interval(0, 127) uint arg4,
>   ref @interval(0, 8192) size_t arg5
> );
>
> The task is create a template, which given a function type like typeof(&fun) returns a new function type with all integer parameters replaced by the smallest type that is big enough to hold the interval specified by the UDA and preserves all storage classes.
>
>
> R hun(
>   scope P1 arg1,
>   return int* arg2,
>   lazy @intetval(-16, 320) short arg3,
>   @interval(0, 127) ubyte arg4,
>   ref @interval(0, 8192) ushort arg5
> );
>
> The way I'd like to go about solving this is like this:
>
> template TightenIntegerParams(Fun)
> {
>   alias TightenIntegerParams =
>     ReturnType!Fun function(
>       staticMap!(
>         TightenParam,
>         Parameters!Fun
>       )
>     );
> }
>
> However the moment staticMap internally does `Args[i]` in order to pass the i-th element to the mapping function `F` we lose all information about the storage classes and UDAs.

I now saw that Manu clarified what he meant.

Also I do realize that what we actually need is for `Parameters!fun[0]` to return a parameter object that includes not just the type but also the parameter name, UDAs attached and storage classes. If for example `ref` was a type qualifier, or just otherwise preserved by `alias`-ing it would help, but not solve the whole issue.
July 30, 2020
On Thu, Jul 30, 2020 at 3:45 PM Petar via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Thursday, 30 July 2020 at 04:56:55 UTC, Petar Kirov [ZombineDev] wrote:
> > On Wednesday, 29 July 2020 at 16:31:08 UTC, Jean-Louis Leroy wrote:
> >> [...]
> >
> > My point, that you can't do much useful processing with `__parameters` (except pass all of subset of it), still stands.
> >
> > Say you have a function with N parameters. Some of those parameters are integers and may have a UDA attached to them that specifies the minimum and maximum value they may receive. For example:
> >
> > R fun(
> >   scope P1 arg1,
> >   return int* arg2,
> >   lazy @intetval(-16, 320) long arg3,
> >   @interval(0, 127) uint arg4,
> >   ref @interval(0, 8192) size_t arg5
> > );
> >
> > The task is create a template, which given a function type like typeof(&fun) returns a new function type with all integer parameters replaced by the smallest type that is big enough to hold the interval specified by the UDA and preserves all storage classes.
> >
> >
> > R hun(
> >   scope P1 arg1,
> >   return int* arg2,
> >   lazy @intetval(-16, 320) short arg3,
> >   @interval(0, 127) ubyte arg4,
> >   ref @interval(0, 8192) ushort arg5
> > );
> >
> > The way I'd like to go about solving this is like this:
> >
> > template TightenIntegerParams(Fun)
> > {
> >   alias TightenIntegerParams =
> >     ReturnType!Fun function(
> >       staticMap!(
> >         TightenParam,
> >         Parameters!Fun
> >       )
> >     );
> > }
> >
> > However the moment staticMap internally does `Args[i]` in order to pass the i-th element to the mapping function `F` we lose all information about the storage classes and UDAs.
>
> I now saw that Manu clarified what he meant.
>
> Also I do realize that what we actually need is for `Parameters!fun[0]` to return a parameter object that includes not just the type but also the parameter name, UDAs attached and storage classes. If for example `ref` was a type qualifier, or just otherwise preserved by `alias`-ing it would help, but not solve the whole issue.
>

UDA's and default args are in-language concepts, alias/tuples can handle them, and don't require any particularly special meta to handle.


July 30, 2020
On Thursday, 30 July 2020 at 04:56:55 UTC, Petar Kirov [ZombineDev] wrote:

> For example:
>
> R fun(
>   scope P1 arg1,
>   return int* arg2,
>   lazy @intetval(-16, 320) long arg3,
>   @interval(0, 127) uint arg4,
>   ref @interval(0, 8192) size_t arg5
> );
>
> The task is create a template, which given a function type like typeof(&fun) returns a new function type with all integer parameters replaced by the smallest type that is big enough to hold the interval specified by the UDA and preserves all storage classes.
>
>
> R hun(
>   scope P1 arg1,
>   return int* arg2,
>   lazy @intetval(-16, 320) short arg3,
>   @interval(0, 127) ubyte arg4,
>   ref @interval(0, 8192) ushort arg5
> );
>
> The way I'd like to go about solving this is like this:
>
> template TightenIntegerParams(Fun)
> {
>   alias TightenIntegerParams =
>     ReturnType!Fun function(
>       staticMap!(
>         TightenParam,
>         Parameters!Fun
>       )
>     );
> }
>
> However the moment staticMap internally does `Args[i]` in order to pass the i-th element to the mapping function `F` we lose all information about the storage classes and UDAs.

Yes I know. My original problem was:

// from
@attrs ref int foo(@otherattrs virtual!Foo obj1, lazy int var, ref virtual!Bar obj2);

// make:
@attrs ref int foo(@otherattrs Foo obj1, lazy int var, ref Bar obj2) {
    return dispatch(obj1, obj2)(obj1, var, obj2);
}

Back to what you described, is it a real use case? I am thinking of writing an article on function generation for the D blog. I was wondering how often this sort of transformation is needed.



July 30, 2020
On Thursday, 30 July 2020 at 04:56:55 UTC, Petar Kirov [ZombineDev] wrote:
> My point, that you can't do much useful processing with `__parameters` (except pass all of subset of it), still stands.

Yeah, I agree it is hard to do a substitution there. The foo... static map proposal a little while ago Manu wrote about is potentially exciting because it enables a lot of new cases, and I think this is another example.
July 30, 2020
On Thu, Jul 30, 2020 at 10:25 PM Adam D. Ruppe via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Thursday, 30 July 2020 at 04:56:55 UTC, Petar Kirov [ZombineDev] wrote:
> > My point, that you can't do much useful processing with `__parameters` (except pass all of subset of it), still stands.
>
> Yeah, I agree it is hard to do a substitution there. The foo... static map proposal a little while ago Manu wrote about is potentially exciting because it enables a lot of new cases, and I think this is another example.
>

This is actually one of many such known motivators for the DIP.


July 31, 2020
On Wednesday, 29 July 2020 at 20:07:35 UTC, Stefan Koch wrote:
>
> I would be interested an actual use-cases for this?
>
> Where would you use, forward? Where would it be the most pleasant solution, and others would be vastly inferior?

I just ran into a use-case tonight! Maybe. The caveat is that my use case might be pretty easy compared to what Andrei is asking for. My use case requires modifying the function signature a lot, but that can actually be exploited to make my solution easier. I probably only need to forward storage class. It's still kinda leading to code that feels wrong or isn't obvious, so maybe it will help your discussion.

Now for the use-case itself:

I am considering putting a "stringize" method in my types, with a signature (by convention) like so:
@nogc nothrow void stringize(return scope StringVacuum writer) { ... }

(I also plan to support a UFCS-style variant, but let's not get distracted.)

This method allows a type to be converted to a string, much like with "toString", but without a direct implication of heap-based (e.g. GC) memory allocation. If the type's string representation is being consumed by a non-memory source, such as stdout or a file stream, then it should be *possible* to avoid memory allocations entirely, at least in most cases, and with the very technical exception of small stack-allocated buffers for handling things like integer/float stringization (not shown here).

Gee, it would really suck if this were some kind of departure from the norm that caused incompatibilities with existing code and infrastructure. Whoops.

But it's OK, because "stringize" is toString-but-better, so it should be possible to just mechanically derive a "toString" function from every "stringize" function. Whenever this is used, it does abandon the original zero-allocation-conversion-to-string feature, but "toString" never had that to begin with (at least not in the general case; just for static strings and such). Code that acknowledges "stringize" can level-up. Code that depends on "toString" still works. Cool.

Now it would be really nice if the "toString" method generated from a "stringize" method would inherit its compile-time guarantees wherever possible. And sometimes it isn't possible. If "stringize" is 'nothrow', then "toString" based on "stringize" should also be 'nothrow'. However, if "stringize" is 'nogc', this does not automatically provide an @nogc "toString", because "toString" is required to concatenate a bunch of stringize's text fragments, and that concatenation requires GC allocations regardless of how @nogc "stringize" may be.

So a minor step in this process is to forward a permutation of "stringize" to "toString".

Here is more detailed code:
https://pastebin.com/JGfkkTxz
I am going to be in drafting-mode for a while and I haven't tried to compile any of that code. Please expect mistakes; I'm providing this to illustrate intent. I hope it helps.
July 31, 2020
On Friday, 31 July 2020 at 09:10:44 UTC, Chad Joan wrote:
> Whenever this is used, it does abandon the original zero-allocation-conversion-to-string feature, but "toString" never had that to begin with (at least not in the general case; just for static strings and such).

kinda a site node but be aware there are toString overloads that do offer this.

see this for example: https://github.com/dlang/druntime/blob/master/src/object.d#L1995
July 31, 2020
On Thursday, 30 July 2020 at 08:36:26 UTC, Manu wrote:
>> I now saw that Manu clarified what he meant.
>>
>> Also I do realize that what we actually need is for `Parameters!fun[0]` to return a parameter object that includes not just the type but also the parameter name, UDAs attached and storage classes. If for example `ref` was a type qualifier, or just otherwise preserved by `alias`-ing it would help, but not solve the whole issue.
>>
>
> UDA's and default args are in-language concepts, alias/tuples can handle them, and don't require any particularly special meta to handle.

You can't alias default args like: fun(time_t a = time(null))
July 31, 2020
On Friday, 31 July 2020 at 09:10:44 UTC, Chad Joan wrote:
>
> I am considering putting a "stringize" method in my types, with a signature (by convention) like so:
> @nogc nothrow void stringize(return scope StringVacuum writer) { ... }
>

Are you aware that toString already supports this kind of overload?

The documentation for it is under `std.format.formatValue`, so it is a bit easy to miss:

https://dlang.org/phobos/std_format.html#.formatValue