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