Proposal for design of 'scope' (Was: Re: Opportunities for D)

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Fri Jul 11 15:01:58 PDT 2014


On Fri, Jul 11, 2014 at 06:41:47AM +0000, deadalnix via Digitalmars-d wrote:
[...]
> On Thursday, 10 July 2014 at 17:04:24 UTC, H. S. Teoh via Digitalmars-d
> wrote:
> >   - For function parameters, this lifetime is the scope of the
> >   function body.
> 
> Some kind of inout scope seem less limiting. The caller know the
> scope, the callee know that is is greater than itself. It is important
> as local variable in the outer scope of the function have more
> restricted scope and must not be assignable.
> 
> Each parameter have a DIFFERENT lifetime, but it is impossible to tell
> which one is larger from the callee perspective. Thus you must have a
> more complex lifetime definition than grater/smaller lifetime. Yup,
> when you get into the details, quantum effects start to arise.

Looks like we might need to use explicit lifetimes for this. Unless
there's a way to simplify it -- i.e., we don't always need exact
lifetimes, as long as the estimated lifetime is never larger than the
actual lifetime. From the perspective of the callee, for example, if the
lifetimes of both parameters are longer than it can see (i.e., longer
than the lifetimes of its parent lexical scopes) then it doesn't matter
what the exact lifetimes are, it can be treated as an unknown value with
a lower bound, as long as it never tries to assign anything with
lifetime >= that lower bound. The caller already knows what these
lifetimes are from outside, but the function may not need to know.

At least, I'm hoping this kind of simplifications will still allow us to
do what we need, while reducing complexity.


> >>   - An unscoped variable is regarded to have infinite lifetime.
> >>
> 
> So it is not unscoped, but I'm simply nitpicking on that one.

Well, yes, the reason I wrote that line was to make the definition
uniform across both scoped and unscoped types. :)


> >>      - Since a scoped return type has its lifetime as part of its
> >>        type, the type system ensures that scoped values never
> >>        escape their lifetime. For example, if we are sneaky and
> >>        return a pointer to an inner function, the type system will
> >>        prevent leakage of the
> 
> This get quite tricky to define when you can have both this and a
> context pointer. Once again, you get into a situation where you have 2
> non sortable lifetime to handle. And worse, you'll be creating values
> out of that mess :)

Is it possible to simplify this by taking the minimum of the two
lifetimes (i.e. intersection)? Or will that run into unsolvable cases?


> >>- Aggregates:
> >>
> >>   - It's turtles all the way down: members of scoped aggregates
> >>     also have scoped type, with lifetime inherited from the parent
> >>     aggregate. In other words, the lifetime of the aggregate is
> >>     transitive to the lifetime of its members.
> 
> Yes rule for access is transitivity. But the rule to write is
> "antitransitive". It gets tricky when you consider that a member
> variable may have to be able to "extend" the lifetime of one of its
> member.
> 
> IE a member of lifetime B in a value of lifetime A sees it lifetime
> becoming max(A, B). Considering lifetime aren't always sortable (as
> show in 2 examples), this is tricky.
> 
> This basically means that you have to define what happen for non
> sortable lifetime, and what happen for union/intersection of lifetime.
> As you see, I've banged my head quite a lot on that one. I'm fairly
> confident that this is solvable, but definitively require a lot of
> effort to iron out all the details.

Along these lines, I'm wondering if "turtles all the way down" is the
wrong way of looking at it. Consider, for example, an n-level deep
nesting of aggregates. If obj.nest1 is const, then obj.nest1.nest2.x
must also be const, because otherwise we break the const system. So
const is transitive downwards. But if obj.nest1 is a scoped reference
type with lifetime L1, that doesn't necessarily mean obj.nest1.y only
has lifetime L1. It may be a pointer that points to an infinite lifetime
object, for example, so it's not a problem that the pointer goes out of
scope before the object pointed to. OTOH, if obj.nest1 has scope L1,
then obj itself cannot have a longer lifetime than L1, otherwise we may
access obj.nest1 after its lifetime is over. So the lifetime of
obj.nest1 must propagate *upwards* (or outwards).

This means that scope is transitive outwards, which is the opposite of
const/immutable, which are transitive inwards! So it's not turtles all
the way down, but "pigeons all the way up". :-P


> >>- Passing parameters: since unscoped values are regarded to have
> >>  infinite lifetime, it's OK to pass unscoped values into scoped
> >>  function parameters: it's a narrowing of lifetime of the original
> >>  value, which is allowed. (What's not allowed is expanding the
> >>  lifetime of a scoped value.)
> >>
> 
> Get rid of the whole concept of unscopped, and you get rid of a whole
> class of redundant definition that needs to be done.

OK, let's just say unscoped == scope with infinite lifetime. :)


> >>I'm sure there are plenty of holes in this proposal, so destroy
> >>away.  ;-)
> >>
> 
> Need some more iron. But I'm happy to see that some people came up
> with proposal that are close to what I had in mind.
[...]

Maybe what we should do, is to have everyone post their current
(probably incomplete) drafts of what scope should do, so that we have
everything on the table and we can talk about what should be kept, what
should be discarded, etc.. It may be, that the best design is not what
any one of us has right now, but some combination of multiple current
proposals.


T

-- 
Be in denial for long enough, and one day you'll deny yourself of things you wish you hadn't.


More information about the Digitalmars-d mailing list