May 25, 2021

On Tuesday, 25 May 2021 at 11:24:59 UTC, Dennis wrote:

>
// Mike Franklin's proposal:
void move(T)(return(target) T source, ref scope T target)

// Steven Schveighoffer's proposal:
void move(T)(return T source, @__sink ref scope T target)

What do you think?

I prefer Mike's proposal, too. It is more readable to some who isn't that involved in the topic, like me :) for instance.

As a compromise, what about this?:

void move(T)(@__source return T source, @__sink ref scope T target)
void move(T)(@__source("sourceA") return T sourceA, @__sink("targetA") ref scope T targetA, @__source("sourceB") return T sourceB, @__sink("targetB") ref scope T targetB)
May 25, 2021

On Tuesday, 25 May 2021 at 11:46:12 UTC, kinke wrote:

>

On Tuesday, 25 May 2021 at 11:24:59 UTC, Dennis wrote:

>

But there's currently now way of making target the return scope destination when it's not the first parameter. So unless we want to leave move @system or create a move2 function with parameters reversed, we're back at the drawing board for "Extend Return Scope Parameters". Two earlier proposals were:

// Mike Franklin's proposal:
void move(T)(return(target) T source, ref scope T target)

// Steven Schveighoffer's proposal:
void move(T)(return T source, @__sink ref scope T target)

What do you think?

Good overview, thanks for the post. If we don't really need a generic solution for that, i.e., can expect new code to use the first param and move to be the only problematic existing code,

This sounds like a really annoying and arbitrary limitation. Among other things, it interferes with the API designer's ability to freely choose which parameter they want to serve as the pseudo-this for UFCS.

Being able to make that choice is important both for aesthetic reasons, but more importantly to enable "extension methods" that allow writing generic code that accepts both custom types, and built-in types which are not otherwise extensible.

Also, what about C interop? APIs written in C often contain functions which otherwise obey the semantics of scope and return, but will not respect arbitrary limitations on the order of parameters.

May 26, 2021

On Tuesday, 25 May 2021 at 11:24:59 UTC, Dennis wrote:

>

Background

The return attribute allows you to return a scope variable.
The compiler knows that the returned value has the same lifetime as the argument passed to the function.

// ┌───────────<──────────┐
int* identity(return int* x) @safe {
    return x;
}

[...]

These are very good questions.

May 26, 2021

On Tuesday, 25 May 2021 at 22:19:23 UTC, tsbockman wrote:

>

On Tuesday, 25 May 2021 at 11:46:12 UTC, kinke wrote:

>

On Tuesday, 25 May 2021 at 11:24:59 UTC, Dennis wrote:

>

But there's currently now way of making target the return scope destination when it's not the first parameter. So unless we want to leave move @system or create a move2 function with parameters reversed, we're back at the drawing board for "Extend Return Scope Parameters". Two earlier proposals were:

// Mike Franklin's proposal:
void move(T)(return(target) T source, ref scope T target)

// Steven Schveighoffer's proposal:
void move(T)(return T source, @__sink ref scope T target)

What do you think?

Good overview, thanks for the post. If we don't really need a generic solution for that, i.e., can expect new code to use the first param and move to be the only problematic existing code,

This sounds like a really annoying and arbitrary limitation.

It's not arbritrary at all - the purpose is to avoid this:

https://carols10cents.github.io/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures

Reasonable people of course can disagree on whether or not avoiding that is a good idea.

May 26, 2021

On Wednesday, 26 May 2021 at 12:36:42 UTC, Atila Neves wrote:

>

[snip]

It's not arbritrary at all - the purpose is to avoid this:

https://carols10cents.github.io/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures

Reasonable people of course can disagree on whether or not avoiding that is a good idea.

I also couldn't help but think of Rust's lifetime annotations when reading the OP's post. Rust's is more expressive though. If you try to limit it, then there obviously will be cases that you can't cover everything without making it more complicated. I can't help but think that return should be something like lifetime(return) instead. Then you can have lifetime(variableA), lifetime(variableB). It's a bit more confusing to have return(variableA)/return(variableB).

May 26, 2021

On Tuesday, 25 May 2021 at 13:18:16 UTC, zjh wrote:

Maybe we can simplify it with a symbol @(a)/@(b), then @a/@b, and so on.Indicating the direction of the pointer to track the direction of the pointer(A-->B).

May 26, 2021

On Wednesday, 26 May 2021 at 13:01:24 UTC, jmh530 wrote:

>

On Wednesday, 26 May 2021 at 12:36:42 UTC, Atila Neves wrote:

>

[snip]

It's not arbritrary at all - the purpose is to avoid this:

https://carols10cents.github.io/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures

Reasonable people of course can disagree on whether or not avoiding that is a good idea.

I also couldn't help but think of Rust's lifetime annotations when reading the OP's post. Rust's is more expressive though. If you try to limit it, then there obviously will be cases that you can't cover everything without making it more complicated. I can't help but think that return should be something like lifetime(return) instead. Then you can have lifetime(variableA), lifetime(variableB). It's a bit more confusing to have return(variableA)/return(variableB).

D's general philosophy w.r.t. memory safety so far has been to try and balance ease of use and comprehension with expressiveness. D's scope and return are less expressive than Rust's lifetimes, but that expressiveness may be worth giving up if it's easier to write correct @safe and DIP1000-compliant code than it is to write correct Rust code.

Of course, D's vision here is severely hampered in practice by the poor quality of its documentation (raise your hand if you can explain what "return ref parameter semantics with additional scope parameter semantics" actually means). But that's the idea.

May 26, 2021

On Wednesday, 26 May 2021 at 15:29:32 UTC, Paul Backus wrote:

>

[snip]

D's general philosophy w.r.t. memory safety so far has been to try and balance ease of use and comprehension with expressiveness. D's scope and return are less expressive than Rust's lifetimes, but that expressiveness may be worth giving up if it's easier to write correct @safe and DIP1000-compliant code than it is to write correct Rust code.

Correct me if I'm wrong, but Rust bakes in D's scope to everything unless you override it with a lifetime annotation. So I don't think the issue is scope per se. return is what is used to tie the lifetime of a parameter to the lifetime of the output. I agree that in the simple example it is easier to write correct code.

>

Of course, D's vision here is severely hampered in practice by the poor quality of its documentation (raise your hand if you can explain what "return ref parameter semantics with additional scope parameter semantics" actually means). But that's the idea.

+1

May 26, 2021

On Wednesday, 26 May 2021 at 12:36:42 UTC, Atila Neves wrote:

>

It's not arbritrary at all - the purpose is to avoid this:

https://carols10cents.github.io/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures

Reasonable people of course can disagree on whether or not avoiding that is a good idea.

Proposal: Solve the problem incrementally. Start with a single pre-named routing channel (i.e. lifetime) supported directly by the compiler. Call it @__in1 and @__out1 (these are parameter attributes). The compiler will track this lifetime in addition to the scope and return lifetimes. It’s possible that this one additional channel will solve all use cases.

If later on, someone provides an undeniably compelling use case for additional lifetime channels, simply add @__in2 and @__out2, @__in3 and @__out3, etc. as the case requires.

The assumption upon which this solution is based is that Rust’s user-named "dictionary of lifetimes" is complete and total OVERKILL, that the actual number of lifetimes a function ever really must track is so small that the tracking mechanism can be incrementally baked into the compiler itself. (If I’m not mistaken, a baked-in lifetime requires only two bits of compiler memory per parameter to track, and only one bit for other variables.)

For the most part, users would hardly ever see @__in1 and @__out1 since they would be inferred by templates, and rarely used anyway. But for cases like phobos’ move, the facility would be available.

Hope you like my suggestion.

Zach

May 27, 2021

On Wednesday, 26 May 2021 at 12:36:42 UTC, Atila Neves wrote:

>

On Tuesday, 25 May 2021 at 22:19:23 UTC, tsbockman wrote:

>

On Tuesday, 25 May 2021 at 11:46:12 UTC, kinke wrote:

>

Good overview, thanks for the post. If we don't really need a generic solution for that, i.e., can expect new code to use the first param and move to be the only problematic existing code,

This sounds like a really annoying and arbitrary limitation.

It's not arbritrary at all - the purpose is to avoid this:

https://carols10cents.github.io/book/ch10-03-lifetime-syntax.html#lifetime-annotations-in-function-signatures

Why must it always be the first parameter, instead of always being the last parameter, etc.? The choice of which parameter gets special treatment is arbitrary, and is the part I was complaining about because of how it interacts with UFCS.