ENB: Using a more functional style

24 views
Skip to first unread message

Edward K. Ream

unread,
Apr 1, 2021, 8:38:57 AM4/1/21
to leo-editor
This Engineering Notebook post discusses how to improve Leo's code by using a more functional programming style, particularly regarding Leo's generators.

If I were to start Leo today, I would define the methods of the Position class differently. Don't panic: I'm not going to break existing Leonine scripts.

Let's look at an example.

Changes to leoRst.py

I have just changed the main loops in leoRst.py so that they use a more functional style. The new code is in the ekr-new-rst branch, along with the rest of the new rst3 code.

The old code used the pattern:

p = p.copy()
after = p.nodeAfterTree()
while p and p != after:
    << process p, and advance p >>

The new code uses the pattern:

for p in root.self_and_subtree():
    << process p, leaving p unchanged>>

In other words, the old code depends on side effects; the new code does not.

A more functional Position class

If I were designing the Position class today, I would make the following changes:

- Remove (or hide) all p.moveTo* methods.
- Remove (or hide) p.copy()
- Eliminate the evil "copy" kwarg from all iterators.

The (approximate) effect of these changes would be to make positions immutable. Alas, immutable positions do not automatically make it easier to insert, delete or move nodes. In particular, c.deletePositionsInList would remain exactly as before.

Performance issues

I have glossed over a minor performance issue. The old code skipped @rst-ignore-tree nodes in the outer loop. The new code skips nodes further down in the calling tree by testing `self.in_ignore_tree(p)` for every node in the tree. self.in_ignore_tree(p) must search up the tree, so we expect O(n*log(n)) total time to make all the calls to self.in_ignore_tree(p).

There are easy workarounds for this supposed performance hit. For example, a prepass could mark to-be-ignored nodes. The time to perform all the tests would then be O(n).

In practice, such performance problems will never be a problem anywhere in Leo. Indeed, I expect similar or better performance for functional code. Python's generators are super fast.

Summary

Converting to a more functional style is possible now. Just remove calls to p.moveTo* and adjust accordingly. Bye bye side effects. From time to time, I'll consider actually making those changes.

If I were designing Leo's Position class today, I would eliminate or hide the p.moveTo* methods, the p.copy method, and the copy kwarg for all of Leo's generators. These changes would make positions immutable. However, the complications involved in changing outlines would remain.

I will make no changes to the Position class that would break existing scripts. The p.moveTo* and copy methods will remain. I may tweak the Position class, but such changes will be minor.

All comments welcome.

Edward

Edward K. Ream

unread,
Apr 1, 2021, 9:15:55 AM4/1/21
to leo-editor
On Thursday, April 1, 2021 at 7:38:57 AM UTC-5 Edward K. Ream wrote:

If I were designing Leo's Position class today, I would eliminate or hide the p.moveTo* methods, the p.copy method, and the copy kwarg for all of Leo's generators. These changes would make positions immutable.

These changes would also eliminate all tests of the form `if p` or `if not p` and would moot the assignment p.v = None in various moveTo* methods. Again, I am unlikely ever to prohibit tests on p. There is not nearly enough reason to break existing user scripts.

Instead, within Leo's core, I'll adopt the attitude that positions should be treated as immutable.

Edward

Edward K. Ream

unread,
Apr 1, 2021, 10:41:21 AM4/1/21
to leo-editor
On Thursday, April 1, 2021 at 8:15:55 AM UTC-5 Edward K. Ream wrote:

If I were designing Leo's Position class today, I would eliminate or hide the p.moveTo* methods, the p.copy method, and the copy kwarg for all of Leo's generators. These changes would make positions immutable.

Or maybe not. the p.moveTo* methods are kinda like progn in lisp. They allow an escape hatch to non-functional programming.  In any case, the question is moot: Leo's Position class is going to stay as it is.

Edward
Reply all
Reply to author
Forward
0 new messages