February 07, 2013
On 2/7/2013 3:16 AM, deadalnix wrote:
>>> The 'funny' things is that C#'s would cause syntax problem issue with address
>>> of,
>>> where D does.
>>
>> I don't understand this sentence.
>>
>
> In C#, foo.bar won't execute bar's method of foo. It will get what is called in
> D a delegate. foo.bar() execute that delegate.
>
> It remove the need for &foo.bar altogether, so with that mechanism in D, no
> problem with & operator.
>
> Scala does even one step further by executing the method, or not, depending on
> what is expected, proving that optional () are compatible with.
>
> Back to D, we have void function() foo; foo is declared as a function here.

No, that declares foo as a "pointer to function".


> Now you can pass several object to templates alias parameters, that will behave
> differently in slightly different subtle ways. For no real good reason.

They won't behave any differently than they would outside of being passed as aliases. I don't see a special problem added with alias parameters.


>>> I don't think it is, and looking at other languages choices, I'm not alone.
>>
>> Given the vast variety and complexity of languages out there, for every
>> example of A you can find, I can find examples of !A. It's not really useful,
>> after all, if other languages got it all right, there's no point to D.
> If A is the instruction pack entity type mentionned above, you'll find pretty
> hard to find a !A if we exclude C and C++ (well and D, but it is kind of
> excluded if look for references). I don't pretend none exists, but it seems
> pretty hard to find one.

There are few languages in the systems programming category (languages that give one full access to pointers), so excluding C and C++ seems rather draconian.


> He does so by adding special cases. That is the problem with special cases, they
> multiply themselves faster than rabbits.

Taking the address of a ref was already problematic.

February 08, 2013
On Thursday, 7 February 2013 at 19:49:27 UTC, Walter Bright wrote:
> On 2/7/2013 6:15 AM, Zach the Mystic wrote:
>> Can you tell me if you consider my proposal with regard to making ref safe and
>> complete simple and intuitive both for compiler and user, and if not, why? (I
>> don't have an opinion about '&' yet.)
>>
>> http://forum.dlang.org/thread/ket199$2c27$1@digitalmars.com#post-bsgrtofehxfjmzkaedfr:40forum.dlang.org
>>
>
> I don't know if it's complete and safe. It's not something that can be determined with a quick read.

Okay. But at least that's a 'check' on simple and intuitive?

As far as why I think it's safe, my investigation led me to conclude that the key question was, how do I know whether to treat the return value of a ref function which accept a ref as local or global? I pass a value into a mysterious box, how do I know whether what comes out the other end is related to what I passed in, or something completely different? All the cases DIP25 takes care of are the ones which can be known just by the peculiarities of the call and signature, but many cases just leave you in the dark. The only way the compiler can know for sure without help from the function signature is by dipping into the function's code to take a look, which seems like too high a price to pay. About a month ago Jonathan M Davis brought this up and pointed out that the PIMPL idiom occasionally banishes altogether that opportunity for the compiler.

After a few tries, I realized that 'out' was actually still available to use for this purpose, which is either pure luck (possible) or a hidden byproduct of the fact that 1) it's a good keyword to begin with or 2) there's a secret truth to its interchangeability with 'ref' which applies to return values as well as to parameter tags.
February 08, 2013
On Thursday, 7 February 2013 at 14:43:38 UTC, Steven Schveighoffer wrote:
> On Thu, 07 Feb 2013 01:06:34 -0500, Walter Bright <newshound2@digitalmars.com> wrote:
>
>>
>> The only time (now) that you can take the address of function return value is if that is a return by ref. So, if taking the address of a ref is disallowed, then the syntax is no longer ambiguous.
>
> Thinking about this, I don't know that I like the idea of disallowing taking the address of ref.
>
> One major usage of taking the address of ref returns is the opIndex operator:
>
> int *ptr = &arr[0];
>
> Or, more generally, the front property of a range:
>
> int *ptr = &arr.front;
>
> What I am concerned about is that this is not going to have the desired effect.  Instead of grudgingly switching to a new style of coding, people will simply return pointers instead of ref.
>

Exactly.
February 08, 2013
On Thursday, 7 February 2013 at 19:57:50 UTC, Walter Bright wrote:
> On 2/7/2013 3:16 AM, deadalnix wrote:
>>>> The 'funny' things is that C#'s would cause syntax problem issue with address
>>>> of,
>>>> where D does.
>>>
>>> I don't understand this sentence.
>>>
>>
>> In C#, foo.bar won't execute bar's method of foo. It will get what is called in
>> D a delegate. foo.bar() execute that delegate.
>>
>> It remove the need for &foo.bar altogether, so with that mechanism in D, no
>> problem with & operator.
>>
>> Scala does even one step further by executing the method, or not, depending on
>> what is expected, proving that optional () are compatible with.
>>
>> Back to D, we have void function() foo; foo is declared as a function here.
>
> No, that declares foo as a "pointer to function".
>

That isn't what I read. It is clearly written void function() foo; And if in int a; a is an int and in int* b, b is a pointer on int, foo must be a function. The fact that this very entity type is called function pointer is C is irrelevant.

The distinction between the 2 is a problem in the first place, as it create new entities with different behavior, for a benefice that is unclear to me.

>> Now you can pass several object to templates alias parameters, that will behave
>> differently in slightly different subtle ways. For no real good reason.
>
> They won't behave any differently than they would outside of being passed as aliases. I don't see a special problem added with alias parameters.
>

Because the same syntax have different meanings, so suddenly, you have to special case everything. That is never done in practice, not even in phobos.

>> If A is the instruction pack entity type mentionned above, you'll find pretty
>> hard to find a !A if we exclude C and C++ (well and D, but it is kind of
>> excluded if look for references). I don't pretend none exists, but it seems
>> pretty hard to find one.
>
> There are few languages in the systems programming category (languages that give one full access to pointers), so excluding C and C++ seems rather draconian.
>

I don't see why this should be limited to system languages. This is about function and most languages have functions (and most modern languages have first class functions).

Additionally, as show with scala or C#, the semantic of theses language often do not conflict with pointer access.

>> He does so by adding special cases. That is the problem with special cases, they
>> multiply themselves faster than rabbits.
>
> Taking the address of a ref was already problematic.

Yes indeed. I never argued against that.
February 08, 2013
On 02/06/2013 02:38 AM, Andrei Alexandrescu wrote:
> Probably it'll need a fair amount of tweaking. Anyhow it's in
> destroyable form.
>
> http://wiki.dlang.org/DIP25
>
>
> Thanks,
>
> Andrei

A single delegate (closure) can be used to defer both reads and writes on an arbitrary expression.

This works on naked variables, references, pointers, properties, and probably a number of other things I haven't thought of yet.

Here's the relevance to DIP25: closures already have well-defined escape semantics.  The downside is that they probably allocate heap way too aggressively in the current implementation.  The upshot is that they are always memory-safe.  This makes optimization a quality-of-implementation issue: a sufficiently intelligent compiler should be able to remove many allocations for non-escaping closures.

Perhaps ref parameters should be delegates under the hood instead of pointers?

The longterm disadvantage I see with this is the extra pointer that must be passed/returned.  I wonder how hard it would be for the compiler to instantiate specialized oldschool-pointer versions of functions with ref parameters whenever calls are made that do not require the guarantees that closures provide.

A working demonstration is given below.

Destroy!


import std.traits;
import std.stdio;

struct Option(T)
{
    bool hasValue = false;
    union
    {
        ubyte nope;
        T value;
    }

    this( T value )
    {
        hasValue = true;
        this.value = value;
    }
}

Option!T none(T)()
{
    Option!T result;
    return result;
}

/* For some reason we need to cast to this to make template deduction work on any functions it gets passed into. */
template DelegateRef(T)
{
    alias T delegate(Option!T intake) DelegateRef;
}

template isDelegateRef(T)
{
    static if ( isDelegate!T )
    {
        alias ReturnType!T R;
        static if ( is( T == R delegate(Option!R) ) )
            enum isDelegateRef = true;
        else
            enum isDelegateRef = false;
    }
    else
        enum isDelegateRef = false;
}

string accessor(string expr)
{
    return
        `cast(DelegateRef!(typeof(`~expr~`))) (`~
        `delegate typeof(`~expr~`)(Option!(typeof(`~expr~`)) intake) {`~
        `    if ( intake.hasValue )`~
        `        return (`~expr~` = intake.value);`~
        `    else`~
        `        return `~expr~`;`~
        `})`;
}

// Function that accepts a reference.
auto someFunc(Q)(Q qux) if ( isDelegateRef!Q )
{
    alias ReturnType!Q T;
    auto x = qux(none!T);
    x |= 0xF00D;
    qux(Option!T(x));
    return qux(none!T);
}

struct MyStruct
{
    private int m_q;

    @property int q()
    {
        writefln("get q (%x)",m_q);
        return m_q;
    }

    @property int q(int v)
    {
        writefln("set q (%x = %x)", m_q, v);
        return m_q = v;
    }
}

void testRef( ref int foo )
{
    assert(someFunc(mixin(accessor("foo"))) == 0xF00D);
}

void testPtr( int* foo )
{
    assert(someFunc(mixin(accessor("*foo"))) == 0xF00D);
}

void main()
{
    int abc = 0;
    assert(someFunc(mixin(accessor("abc"))) == 0xF00D);
    assert(abc == 0xF00D);

    int foo = 0;
    testRef(foo);
    assert(foo == 0xF00D);

    int bar = 0;
    testPtr(&bar);
    assert(bar == 0xF00D);

    MyStruct s;
    s.q = 0;
    assert(someFunc(mixin(accessor("s.q"))) == 0xF00D);
    assert(s.q == 0xF00D);
}

February 08, 2013
On Friday, 8 February 2013 at 06:28:54 UTC, Chad Joan wrote:
> On 02/06/2013 02:38 AM, Andrei Alexandrescu wrote:
>> Probably it'll need a fair amount of tweaking. Anyhow it's in
>> destroyable form.
>>
>> http://wiki.dlang.org/DIP25
>>
>>
>> Thanks,
>>
>> Andrei
>
> A single delegate (closure) can be used to defer both reads and writes on an arbitrary expression.
>
> This works on naked variables, references, pointers, properties, and probably a number of other things I haven't thought of yet.
>
> Here's the relevance to DIP25: closures already have well-defined escape semantics.  The downside is that they probably allocate heap way too aggressively in the current implementation.  The upshot is that they are always memory-safe.  This makes optimization a quality-of-implementation issue: a sufficiently intelligent compiler should be able to remove many allocations for non-escaping closures.
>
> Perhaps ref parameters should be delegates under the hood instead of pointers?
>
> The longterm disadvantage I see with this is the extra pointer that must be passed/returned.  I wonder how hard it would be for the compiler to instantiate specialized oldschool-pointer versions of functions with ref parameters whenever calls are made that do not require the guarantees that closures provide.
>
> A working demonstration is given below.
>
> Destroy!
>

The cost of passing a delegate is way higher than the cost of passing the extra pointer. Delegate cause an opaque function call so :
 - All registers that the callee is allowed to trash must be saved preventively (even if the callee don't trash them).
 - CPU cannot really use branch prediction.
 - The compiler must assume that the delegate call may have arbitrary side effects so have to commit everything to memory and then take everything back afterward. This prevent many instruction reordering, register promotions, dead read/write elimination, constant propagation and so on.

Considering passing by ref is sometime done to fasten things, this defeat the whole point.

Note that the operation above are not dying slow, but clearly not a good fit for ref.

>
> import std.traits;
> import std.stdio;
>
> struct Option(T)
> {
>     bool hasValue = false;
>     union
>     {
>         ubyte nope;
>         T value;
>     }
>
>     this( T value )
>     {
>         hasValue = true;
>         this.value = value;
>     }
> }
>
> Option!T none(T)()
> {
>     Option!T result;
>     return result;
> }
>
> /* For some reason we need to cast to this to make template deduction work on any functions it gets passed into. */
> template DelegateRef(T)
> {
>     alias T delegate(Option!T intake) DelegateRef;
> }
>
> template isDelegateRef(T)
> {
>     static if ( isDelegate!T )
>     {
>         alias ReturnType!T R;
>         static if ( is( T == R delegate(Option!R) ) )
>             enum isDelegateRef = true;
>         else
>             enum isDelegateRef = false;
>     }
>     else
>         enum isDelegateRef = false;
> }
>
> string accessor(string expr)
> {
>     return
>         `cast(DelegateRef!(typeof(`~expr~`))) (`~
>         `delegate typeof(`~expr~`)(Option!(typeof(`~expr~`)) intake) {`~
>         `    if ( intake.hasValue )`~
>         `        return (`~expr~` = intake.value);`~
>         `    else`~
>         `        return `~expr~`;`~
>         `})`;
> }
>
> // Function that accepts a reference.
> auto someFunc(Q)(Q qux) if ( isDelegateRef!Q )
> {
>     alias ReturnType!Q T;
>     auto x = qux(none!T);
>     x |= 0xF00D;
>     qux(Option!T(x));
>     return qux(none!T);
> }
>
> struct MyStruct
> {
>     private int m_q;
>
>     @property int q()
>     {
>         writefln("get q (%x)",m_q);
>         return m_q;
>     }
>
>     @property int q(int v)
>     {
>         writefln("set q (%x = %x)", m_q, v);
>         return m_q = v;
>     }
> }
>
> void testRef( ref int foo )
> {
>     assert(someFunc(mixin(accessor("foo"))) == 0xF00D);
> }
>
> void testPtr( int* foo )
> {
>     assert(someFunc(mixin(accessor("*foo"))) == 0xF00D);
> }
>
> void main()
> {
>     int abc = 0;
>     assert(someFunc(mixin(accessor("abc"))) == 0xF00D);
>     assert(abc == 0xF00D);
>
>     int foo = 0;
>     testRef(foo);
>     assert(foo == 0xF00D);
>
>     int bar = 0;
>     testPtr(&bar);
>     assert(bar == 0xF00D);
>
>     MyStruct s;
>     s.q = 0;
>     assert(someFunc(mixin(accessor("s.q"))) == 0xF00D);
>     assert(s.q == 0xF00D);
> }

February 08, 2013
On 02/08/2013 02:06 AM, deadalnix wrote:
> On Friday, 8 February 2013 at 06:28:54 UTC, Chad Joan wrote:
>>
>> Destroy!
>>
>
> The cost of passing a delegate is way higher than the cost of passing
> the extra pointer. Delegate cause an opaque function call so :
> - All registers that the callee is allowed to trash must be saved
> preventively (even if the callee don't trash them).
> - CPU cannot really use branch prediction.
> - The compiler must assume that the delegate call may have arbitrary
> side effects so have to commit everything to memory and then take
> everything back afterward. This prevent many instruction reordering,
> register promotions, dead read/write elimination, constant propagation
> and so on.
>
> Considering passing by ref is sometime done to fasten things, this
> defeat the whole point.
>
> Note that the operation above are not dying slow, but clearly not a good
> fit for ref.
>

Fair enough.

I wonder if we can learn something from it though.  It does seem to provide a conceptual model that's very desirable.
February 09, 2013
Two more things:

Disable a read/write propa like int getSetSomeInteger(int).
int getSomeInteger() and void setSomeIntger(int) only allowed.

And disable default parameter for setter.

February 10, 2013
Am Wed, 06 Feb 2013 22:11:40 -0800
schrieb Walter Bright <newshound2@digitalmars.com>:

> On 2/6/2013 4:23 PM, Andrej Mitrovic wrote:
> > On 2/6/13, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> >> Pointers are what they are, and they're not appropriate for code that offers guarantees.
> >
> > But we're talking about @system here. I would probably be fine with these rules if they only applied to @safe.
> >
> > I feel like someone is trying to put training wheels on my mountain bike.
> >
> 
> One reason why @safe is not the default.

It would also be very very nice, if we could at least write "Hello, world!" in @safe D.

-- 
Marco

February 10, 2013
On Wednesday, 6 February 2013 at 21:40:00 UTC, Andrei Alexandrescu wrote:
> On 2/6/13 3:02 PM, Andrej Mitrovic wrote:
>> Also the DIP argues that addressOf solves the @property issue w.r.t.
>> return values. I've proposed we use an .addressOf property which only
>> works on @property functions, and I saw no arguments against it.
>
> There aren't, but a library approach is better than a magic work, all other things being equal.
>
>
> Andrei

struct S
{
  @property int var();
  @property void var(int);
}

The .addressOf property gave me the idea of solving the getter/setter issue, by having two properties...

var.getter
var.setter

maybe it could be added to your library approach though?

1 2 3 4 5 6 7 8
Top | Discussion index | About this forum | D home