RFC: scope and borrowing

Manu via Digitalmars-d digitalmars-d at puremagic.com
Tue Sep 23 05:02:15 PDT 2014


On 23 September 2014 21:02, via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
> On Monday, 22 September 2014 at 15:54:23 UTC, Manu via Digitalmars-d wrote:
>>
>> We arrive at yet another case of "it should have been that way from the
>> start" wrt 'scope'.
>> The baggage of annotation, and the lack of annotation to existing code is
>> a
>> pretty big pill to swallow.
>> If it were just part of the type, there would be no problem, T would
>> already be 'scope T' in the cases where you expect. I can't see any
>> disadvantages there.
>
>
> But it has very different semantics. You cannot expect the same code to work
> for scope and non-scope alike. And it is to be expected  that you have to
> adjust existing code if you want to take advantage of a new feature. If by
> "it should have been that way from the start" you mean that scope should be
> the default, well yes, but we're in the same situation with immutable by
> default, pure by default, @safe by default, nothrow by default, ... That
> ship has sailed, unfortunately.

You can't expect the same code to work for int and immutable(int)
alike either... or int and int[], they all have to be handled somewhat
explicitly.
Generic code shouldn't try to work with is(T == scope(U), U) if the
code doesn't support scope. If the generic code is to support scope,
then the generic code either specifies it's argument to be scope(T),
which works the same as const(T) wrt mutability, or it detects and
handles is(T == scope(U), U) internally.

And yes, that's what I meant by 'should have been that way from the start' :)


>> This would be another band-aid to a core problem. D already has plenty of
>> these.
>> I hear this "perfect forwarding" concept thrown around, and I think it's
>> another faulty concept. I rarely want 'perfect' forwarding... why would I
>> be forwarding in the first place if it's 'perfect' (there are cases, but
>> not so common)? I almost always want *imperfect* forwarding; that is, some
>> small detail(/s) about the forwarding are manipulated. I think this is the
>> primary case where storage class falls apart in concept.
>
>
> That is a straw man. Of course, perfect forwarding needs to allow for
> imperfect forwarding, too. It would indeed be not useful if you couldn't
> inspect and modify the types (and storage classes) of your parameters.

Well you suggested like perfect forwarding was a discrete problem to solve.
Imperfect forwarding implies composing a signature from a complex set
of typeof(), alias, template arguments, traits, etc. We can't be
losing ref-ness in any of these cases, or the forwarding is broken.
scope likewise.
(im)perfect forwarding would work just fine right now if there wasn't
random bits of information that were separated from the type system.


>> Maybe you can give counter examples too, if you think it doesn't work.
>>>
>>>
>>
>> It's complex and time consuming to do so. The situations where it all
>> breaks down are often fairly complex (probably why 'ref' as a storage
>> class
>> seems like an okay idea at face value, although I still don't understand
>> the advantage conceptually), and they tend to appear when you don't expect
>> it. My examples with ref above are all practically applicable to scope too
>> though.
>
>
> A concrete example would still be very helpful to understand your point of
> view. I.e., not only the concrete wrapper type/function, but also how you
> would want to use it, and why it wouldn't work without scope being part of
> the type.

Generating function signatures is certainly the simplest and also the
most common example of these problems.
The problem is simple; if it's not part of the type, then the only
solution is a static if with explicit detection for the thing, and
code duplication with/without the non-type attribute.
Templates are rarely flat either, they are usually a composition of
other templates. Everything in the chain needs to have special case
handling for ref (and scope) that lives outside the type system.

>> Let's turn this around... Why the complexity? Why would you make the
>> change
>> to your proposal to make 'scope' something else outside of the type
>> system?
>
>
> Well, I think Ivan gave an excellent example (ElementType) why it should be
> separate from the type. Type modifiers currently only deal with mutability
> (and the related shared). There can be some nasty surprises if we add
> another concept there.

I don't think shared is really related, also what about *, [], [n],
etc. these are all type modifiers that cause the same sort of
'surprises'.
We have tools for dealing with all of these: Unqual!T,
PointerTarget!T, isPointer!T, isArray!T, etc... they all work well, I
haven't had any complaints about them personally. scope detection
would have no significant impact on the mix.

We also have tools like is(T : U), which should be extended to work as
expected with scope.
In the same way as "is(X : const(T)) == true" works for T == mutable,
immutable, or const, we would have the same is(X : scope(T)) to detect
if the lifetimes are compatible.
We need scope in the type system so we can make use of all the
powerful type manipulation features that D has. As far as I'm
concerned, D's type system *IS* D. (this is also true for ref)


>> What is the advantage to that complexity. D has no structured method for
>> dealing with that sort of meta, we only have types. Beyond that, it's just
>> spaghetti, as we learn from ref.
>
>
> Then it's better to introduce such a method, IMO.

Please no. D does NOT need a parallel suite of syntax to deal with
'storage class' meta.


More information about the Digitalmars-d mailing list