[OT] What are D's values?

Steven Schveighoffer schveiguy at gmail.com
Fri Oct 8 15:35:26 UTC 2021


On 10/7/21 1:35 PM, H. S. Teoh wrote:
> On Wed, Oct 06, 2021 at 11:59:46AM -0400, Steven Schveighoffer via Digitalmars-d wrote:
> [...]
>> There is one place where I have struggled though, and D might be able to do
>> better. And that is with optional parentheses and taking the address. When a
>> property can be either an accessor or a field, then `obj.prop` can work the
>> same. However, `&obj.prop` is not the same.
>>
>> I have solved it by using this stupid function:
>>
>> ```d
>> auto ref eval(T)(auto ref T t) { return t; }
>>
>> // instead of &obj.prop
>> auto ptr = &eval(obj.prop);
>> ```
> 
> IMO, taking the address of something is a low-level operation that
> doesn't really belong in higher-level logic.  Unsurprisingly, therefore,
> it doesn't lend itself well to the malleability that generally applies
> in D code.
> 
> The fact of taking the address of something imposes the implicit
> assumption that that object has an address to be taken in the first
> place.  Meaning that once you use such an operation, the target object
> is no longer Liskov-substitutable with anything else that may not have
> an address (e.g. an accessor function).  So this imposes limitations on
> what kinds of refactoring the code will be amenable to.

To give more context, it is solely for this little function:

```d
auto firstElem(R)(R r) if (hasLvalueElements!R)
{
     if(r.empty)
         return null;
     return &(eval(r.front));
}
```

Why do I need this function? Because Phobos doesn't give me nice access 
to things from `find`. Instead, it gives me a range. Which is useful for 
some cases, but for others, I just want to get reference access of the 
element I was looking for.

So compare the two possibilities:
```d
auto result = someRange.find(elem);
if(!result.empty) {
    if(result.front.x > 5)
        result.front.x -= 5;
}

// vs.

if(auto v = someRange.find(elem).firstElem) {
    if(v.x > 5)
        v.x -= 5;
}
```

For some things, pointers are the best interface.

Sure, I could wrap this up in a type, but it still would need to store a 
pointer internally (or the range itself), plus a pointer is already 
doing exactly what I need it to do.

The main issue is that `obj.method` means different things depending on 
context.

As kind of another similar story, A long time ago, an eponymous template 
allowed some access to things inside the template, and did not always 
map directly to the eponymous member *in some situations*. While this 
allowed greater flexibility, it produced odd behavior. Eventually, all 
access of the internals of an eponymous template were removed, and it 
always *always* just means the eponymous member. This removed some 
expressiveness, but the result is much much cleaner.

I would like to see something similar for property methods.

> [...]
>> While it's nice D has a mechanism to work around this difference, I
>> find having to use such shims a bit awkward. And it's a direct
>> consequence of hiding the implementation of a field/property behind
>> the same syntax. You can find other cases where D can be awkward (such
>> as `typeof(obj.prop)` or checking types regardless of mutability).
>>
>> What is the "better" answer though? I don't know.
> [...]
> 
> Not necessarily a better answer: static if to discover whether something
> is a field or an accessor, and extract the address appropriately,
> possibly encapsulated in a utility function?  Not much different from
> your .eval hack, though.

This would be horrid. Basically, your code starts looking like:

```d
static if(isAccessor!(obj.foo)) // have fun writing this, there's lots 
of traps.
    return &obj.foo();
else
    return &obj.foo;
```

The eval hack is much much cleaner. It means "this is an expression, not 
a symbol". Maybe the answer is to do this with some syntax feature, so I 
can avoid calling silly hack functions.

> The C++ answer is to allow overloading of unary &.  However, that opens
> up a whole 'nother can o' worms that I would not recommend.

Nope, I would never want that. I want a pointer when I ask for a pointer.

-Steve


More information about the Digitalmars-d mailing list