May 10, 2018
On Thursday, 10 May 2018 at 14:37:00 UTC, rikki cattermole wrote:
> On 11/05/2018 2:33 AM, Yuxuan Shui wrote:
>> On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote:
>> 
>> But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.
>
> class -> struct, now it is back to 1 allocation.

Even easier:

alias createDataStructure = (...) => new DataStructure(..., alloc);
May 11, 2018
On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:
> ...
> // constructor of DataStructure
> this(Allocator alloc=__ALLOC__) {...}
> ...
> auto alloc = new SomeAllocator();
> define __ALLOC__ = alloc;
> // And we don't need to pass alloc everytime
> ...
>
> Is this a good idea?

Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D?

I don't know whether it's a good idea all-in-all, but assuming the arguments can be used as compile-time I can already see a big use case: killing autodecoding without breaking code. Something like:

auto front(C, bool disableDecoding = __NODECODE__)(inout C[] string)
{   static if (disableDecoding) {...}
    else {...}
}
May 11, 2018
On Friday, 11 May 2018 at 11:42:07 UTC, Dukc wrote:
> On Thursday, 10 May 2018 at 14:15:18 UTC, Yuxuan Shui wrote:
>> ...
>> // constructor of DataStructure
>> this(Allocator alloc=__ALLOC__) {...}
>> ...
>> auto alloc = new SomeAllocator();
>> define __ALLOC__ = alloc;
>> // And we don't need to pass alloc everytime
>> ...
>>
>> Is this a good idea?
>
> Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D?

Yes it does. I was thinking the exact same thing while watching his talk; implicits would be perfect for allocators.
May 11, 2018
On 10.05.2018 16:22, rikki cattermole wrote:
> On 11/05/2018 2:20 AM, Yuxuan Shui wrote:
>> On Thursday, 10 May 2018 at 14:17:50 UTC, rikki cattermole wrote:
>>> On 11/05/2018 2:15 AM, Yuxuan Shui wrote:
>>>> [...]
>>>
>>> Bad idea, too much magic.
>>
>> This magic is already there in D. I just want to use it in a different way.
> 
> The magic is not already in there.
> 
> __LINE__ and __MODULE__ are special, they are constants recognized by the compiler and are immediately substituted if not specified.

Yes, that's essentially the definition of "magic".
May 11, 2018
On Thursday, May 10, 2018 14:15:18 Yuxuan Shui via Digitalmars-d wrote:
> So in D I can use default argument like this:
>
> int f(int line=__LINE__) {}
>
> And because default argument is expanded at call site, f() will be called with the line number of the call site.
>
> This is a really clever feature, and I think a similar feature can be useful in other ways.
>
> Say I need to construct a bunch of data structure that takes an Allocator argument, I need to do this:
>
> ...
> auto alloc = new SomeAllocator();
> auto data1 = new DataStructure(..., alloc);
> auto data2 = new DataStructure(..., alloc);
> auto data3 = new DataStructure(..., alloc);
> ...
>
> This looks redundant. But if we have the ability to define more special keywords like __LINE__, we can do something like this:
>
> ...
> // constructor of DataStructure
> this(Allocator alloc=__ALLOC__) {...}
> ...
> auto alloc = new SomeAllocator();
> define __ALLOC__ = alloc;
> // And we don't need to pass alloc everytime
> ...
>
> Is this a good idea?

It seems like really risky move, honestly, because it means that the function is then affected by what is and isn't declared within the scope where it's called. __FILE__ and __LINE__ are well-defined as to what they mean. No can declare them to mean something else. You don't have symbol resolution issues or naming conflicts. And they're solving a problem that can't actually be solved without compiler help. However, if you just want to change what arguments get passed to foo within your module, all you have to do is define another foo inside the module and have it forward to the original one with whatever arguments you want. What you're suggesting here seems to introduce name pollution issues without solving anything that can't easily be solved with the language as-is.

- Jonathan M Davis

May 11, 2018
On Friday, 11 May 2018 at 14:26:21 UTC, Jonathan M Davis wrote:
> On Thursday, May 10, 2018 14:15:18 Yuxuan Shui via Digitalmars-d wrote:
>> So in D I can use default argument like this:
>> [...]
>> Is this a good idea?
>
> It seems like really risky move, honestly, because it means that the function is then affected by what is and isn't declared within the scope where it's called. __FILE__ and __LINE__ are well-defined as to what they mean. No can declare them to mean something else. You don't have symbol resolution issues or naming conflicts. And they're solving a problem that can't actually be solved without compiler help. However, if you just want to change what arguments get passed to foo within your module, all you have to do is define another foo inside the module and have it forward to the original one with whatever arguments you want. What you're suggesting here seems to introduce name pollution issues without solving anything that can't easily be solved with the language as-is.
>
> - Jonathan M Davis

I see what you're saying and I agree with you. I think a better way would be to try and extend the `with` syntax to work with arbitrary functions, rather than only objects. That would make it more useful. So something like:

---
void f1(allocator alloc, ...){}
void f2(allocator alloc, ...){}
...
void fn(allocator alloc, ...){}

void main()
{
    with(MyAllocator) {
        f1(...);
        f2(...);
        ...
        fn(...);
    }
}
---
May 11, 2018
On Friday, 11 May 2018 at 15:03:41 UTC, Uknown wrote:
> I see what you're saying and I agree with you. I think a better way would be to try and extend the `with` syntax to work with arbitrary functions, rather than only objects. That would make it more useful. So something like:
>
> ---
> void f1(allocator alloc, ...){}
> void f2(allocator alloc, ...){}
> ...
> void fn(allocator alloc, ...){}
>
> void main()
> {
>     with(MyAllocator) {
>         f1(...);
>         f2(...);
>         ...
>         fn(...);
>     }
> }
> ---

It's not as pretty, and I don't know if it works outside this toy example yet, but you can do:

import std.stdio;

struct Allocator
{
    auto call(alias F, Args...)(Args args)
    {
        return F(this, args);
    }

    void deallocateAll()
    {
        writeln("deallocateAll");
    }
}

void f1(Allocator a, int n) { writeln("f1"); }
void f2(Allocator, string s, double d) { writeln("f2"); }

void main()
{
    with (Allocator())
    {
        scope(exit) deallocateAll;
        call!f1(2);
        call!f2("asdf", 1.0);
    }
}
May 11, 2018
On Thursday, 10 May 2018 at 15:15:03 UTC, Paul Backus wrote:
> On Thursday, 10 May 2018 at 14:37:00 UTC, rikki cattermole wrote:
>> On 11/05/2018 2:33 AM, Yuxuan Shui wrote:
>>> On Thursday, 10 May 2018 at 14:28:39 UTC, JN wrote:
>>> 
>>> But doing it with default argument expansion saves you 1 allocation, has 1 less type, while being just as readable. I think that's a win.
>>
>> class -> struct, now it is back to 1 allocation.
>
> Even easier:
>
> alias createDataStructure = (...) => new DataStructure(..., alloc);

I think one problem with this and Factory, is that you have to create one alias/lambda/factory type for every type that takes an allocator.
May 11, 2018
On Friday, 11 May 2018 at 11:42:07 UTC, Dukc wrote:
> [snip]
>
> Doesn't this basically mean including the implicits Martin Odersky talked about at Dconf in D?
>
> I don't know whether it's a good idea all-in-all, but assuming the arguments can be used as compile-time I can already see a big use case: killing autodecoding without breaking code. Something like:
>
> auto front(C, bool disableDecoding = __NODECODE__)(inout C[] string)
> {   static if (disableDecoding) {...}
>     else {...}
> }

I'm not sure this makes sense or not...but what about instead of implicits, you allow a template to have type erased parameters, basically to optionally mimic the behavior of Java's generics. That way the allocator could be included in the type and checked at compile-time, but it wouldn't be known at run-time (not sure that's a positive or not).
May 15, 2018
On Friday, 11 May 2018 at 18:55:03 UTC, Meta wrote:
> On Friday, 11 May 2018 at 15:03:41 UTC, Uknown wrote:
>> [...]
>
> It's not as pretty, and I don't know if it works outside this toy example yet, but you can do:
>
> import std.stdio;
>
> struct Allocator
> {
>     auto call(alias F, Args...)(Args args)
>     {
>         return F(this, args);
>     }
>
>     void deallocateAll()
>     {
>         writeln("deallocateAll");
>     }
> }
>
> void f1(Allocator a, int n) { writeln("f1"); }
> void f2(Allocator, string s, double d) { writeln("f2"); }
>
> void main()
> {
>     with (Allocator())
>     {
>         scope(exit) deallocateAll;
>         call!f1(2);
>         call!f2("asdf", 1.0);
>     }
> }

I found another alternative to this:

https://godbolt.org/g/3Etims