​​
On Sun, Apr 29, 2018 at 1:51 PM, vitalije <[email protected]> wrote:

​Ok.  This is the "long" response that I promised in two other responses I
have just written in this thread.​

It's really too long, but I want to discuss all the details just once.
This will be pre-writing for a much short LeoU lesson.

To me having most basic data element that can't be instantiated on its own
> is a big issue. While on the other side you think it is not a big deal.
>

​Actually, I've never even considered this question.  It's interesting that
Terry has the same instinct on this question as you do.
​


> However if we examine the VNode class, we would find that c.
> ​​
> hiddenRootNode has been mentioned only once: in
> method findAllPotentiallyDirtyNodes where it is used only to be excluded
> from the results of this method.
>

This statement is misleading. In fact, v.context is essential, as a cff on
hiddenRootNode shows.

​The low-level p._linkAsRoot accesses c.hiddenRootNode via
p.v.context. *Remember
this fact*.

To me, it's obvious that vnodes must know, somehow, about c.hiddenRootNode,
so I had better explain in full detail.  You could call this part of my
legacy. It will make its way into LeoU.

*Performance*

You have to go back 25 years to truly understand how good Leo's
implementation of outlines is.

Leo's design visual *and *data design comes directly from the MORE
outliner.  But the performance of the MORE (and the first versions of Leo)
was O(N**2) when creating or changing clones.  I never saw the MORE code,
but the Leo code simply made copies of trees when cloning them. The
performance of MORE was similar to Leo's, so it's easy to infer that MORE
copied outlines too.

Later versions of Leo did much better, using *two* kinds of outline nodes
(tnodes and vnodes) The present version of Leo collapses all outline data
into a single VNode class. All outline operation now have constant, O(1),
performance.  Moving or changing a outline node just changes a few
"pointers".

I'm reviewing all this because the VNode code is sometimes difficult and
complex, but that complexity is what creates the O(1) performance.  In
other words, the complexity is a *justified* optimization.

*Why VNodes (and Positions) must know about c.hiddenRootNode*

Recall that there is only a single vnode for all clones (all cloned
positions).  This can make talking about clones a bit tricky.

v.parents is the list of all parent *vnodes* of the node. This list will
include c.hiddenRootNode (a vnode) if the node represents a top-level
node/position.

So v is a clone if and only if len(v.parents) > 1.  This is an O(1)
operation.

But for vnodes, there is no such thing as "the" parent of the node.  Only
*positions* have a unique parent (in a traversal).

p.hasParent() is as follows:

def hasParent(self):
    p = self
    return p.v and p.stack

So bool(p.hasParent()) will be False for top-level nodes, even though p.v
is c.hiddenRootNode!  This can't be changed.  Many position methods depend
on this fact.

So what, you ask?  I don't see any use of p.v.context.  The answer is, as I
mentioned before, that p._linkAsRoot *does* use p.v.context.

So if *any* position method needs p.v.context, the v.context ivar is
essential.

Yes, we could provide all *position* methods with a context/c ivar, but
that's less useful because of the many-one relationship between positions
and vnodes.  Given a position p, p.v is its (unique) vnode.  But the
converse is not true.  Given a vnode v, there is *no way *to recover *any*
position p such that p.v == v.

Heh, the vnode could traverse the entire outline looking for positions p
such that p.v == v, but that would require access either to c or the hidden
root vnode!

All this is obvious to me. It deserves to be common knowledge.

*Alternatives*

We could imagine other ways of organizing the position and vnode classes so
as to make all positions and vnodes aware of the root of the vnode tree
*without* requiring the Commands class.  For example, code that
instantiates either vnodes or positions could pass c.hiddenRootVnode
*itself* instead of c.

As Vitalije points out, this would simplify testing to some degree.  But
not enough, imo.  Knowing the commander in which a vnode resides seems
completely natural to me.  Vnodes do *not* live in a vacuum. More
importantly, the context/c ivar is exactly the kind of information that we
*want* the VNode class to have.  It's completely general, and completely
harmless.  And to repeat, positions *require *v.context.

We could define a VnodeTree class that would encapsulate the (hidden) root
vnode of the tree.  Instantiating the VnodeTree class would indeed be a bit
simpler.  But then we would immediately have to connect the VnodeTree
instance to the Commands class, so all we would have done would be to add a
bit more complexity to the Commands class.  It's all pain and no real gain.

*Other comments*

There are some places in Leo code where the execution path depends on
> whether we have a brand new vnode or vnode which is already known (i.e.
> clone).
>

​Rev 6fcb92b82d0027 refactors some of this code and considerably simplifies
it.  In particular, there is a new ​
fc.updateSaxClone method.​


> And test to distinguish those two cases is the presence or absence of the
> node in c.fileCommands.gnxDict. In some cases it doesn't matter if the node
> is already attached to the main tree or not. But in some cases it matters.
> Just instantiating a vnode creates the node instance which is present in
> gnxDict, but is not part of the tree yet. This can lead to mysterious bugs,
> that is hard to spot.
>

​I agree that the fc code is difficult.  But we don't have to change the
vnode class to do so.​


There are cases when one might want to have vnode with perhaps some
> children and grandchildren to keep around in detached state. For example
> settings whose values are subtrees. Also when building and transforming
> tree it is sometimes useful to build it outside of the main tree without
> disturbing gnxDict.
>

​Yes, and the present code does all this.  Iirc, there are commander
methods to copy trees.
​


> It also seem to me that the relation between commander and vnode violates
> logic. There should be no commander without hiddenRootNode and yet node
> can't be created without commander instance. That causes complications in
> initialization process which is complex enough on its own.
>

​I agree that there are problems, but imo Leo just has to live with vnodes
as they are.
​


> Now, how this situation could be avoided:
>
​[snip]​


> This is backward compatible. All scripts that use c.fileCommands.gnxDict
> can continue to work. However if one needs to build temporary tree of nodes
> it can be done cleanly and easily.
>

​I have no objection to instantiating vnodes with context=None in certain
situations.  We just have to ensure that no position is ever going to refer
to the resulting vnode.

Pasting can be also method of vnode as I have demonstrated it in the
> prototype recently.
>

​I don't have the details in my mind, so I won't comment.
​


> Both, copyOutline and pasteOutline would be completely self contained and
> the way they work will be completely hidden from the world outside VNode
> class.
>

​I have no objections as long as positions will never use the v.context
field.  I suspect that is so in the scheme you propose.

​
> We can go even further if we like.
>

​I'm all for simplifying or eliminating the gnxDict.  That's a separate
topic, and a valuable one.

Vitalije, I have faith that you can continue to simplify the code.  Maybe a
pull request would be a good next step, perhaps with a post discussing the
theory of operation. The shorter the proposal, the more likely it is that I
shall understand and approve it.

*Summary*

The position class must have access to c.hiddenRootNode.  That's why
v.context must exist.

It's acceptable to instantiate a vnode with context=None if and *only *if
no position instance will ever refer to it.  Let's call such vnodes
*positionless
vnodes*. This is a new distinction in Leo's history. Imo, it's worth all
the discussion so far.

Positionless vnodes may be useful when cutting/pasting trees.  They may
also help simplify the present ugly gnx-related logic.

Edward

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/leo-editor.
For more options, visit https://groups.google.com/d/optout.

Reply via email to