Skip to content

Instantly share code, notes, and snippets.

@taoeffect taoeffect/PARC.lsp
Last active Dec 24, 2015

Embed
What would you like to do?
Failed attempt to create a "Perfect Automatic Reference Counting" scheme. This file has been edited. The original attempt can be found here: http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=15&t=3151
(define (Node.)
; for the sake of this example, let's say that this function
; is a "constructor" that returns a symbol pointing to a map.
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; DECLARE "METHODS" FOR NODE ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (Node.set-paret this parent)
(setf (:parent this) parent))
(define (Node.set-child this child)
(setf (:child this) child) ; Whatever value used to be in (:child this)
; has its RC decremented, and the new value
; (child) has its RC incremented.
(.set-parent child this))
; like `set-child`, but assumes that we're acting on the "base node"
; and therefore adds the child to the end of the list
(define (Node.add-child this child)
(if-let (last (:last this))
(.set-child last child)
(.set-child this child))
(setf (:last this) child))
;;;;;;;;;;;;;;;;;;;;
;; BEGIN EXAMPLE! ;;
;;;;;;;;;;;;;;;;;;;;
; At this point firstNode doesn't exist, so it doesn't have a RC.
; setup our 5-node circle
(set 'firstNode (Node.)) ; parent is nil
(dotimes (_ 4) ; add 4 children to it
(.add-child firstNode (Node.))) ; (Node.) returns a SYMBOL!
; Let's have a look at the RCs of the objects involved after one
; iteration of the loop above:
;
; (.add-child firstNode (Node.)):
; (.add-child <anon2>:this <anon2>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-----------------------+----+-----------------+-----------------------+
; | 'MAIN:firstNode | 1 | 'Node.1 | 'Node.1 : 0->1 |
; | 'anon2:this (Symbol) | 1 | 'MAIN:firstNode | 'MAIN:firstNode: 1->2 |
; | 'anon2:child (Symbol) | 1 | 'Node.2 | 'Node.2 : 0->1 |
;
; WHILE FUNCTION RUNS:
; (.set-child this child):
; (.set-child <anon3>:this <anon3>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-----------------------+----+--------------+--------------------+
; | 'anon3:this (Symbol) | 1 | 'anon2:this | 'anon2:this : 1->2 |
; | 'anon3:child (Symbol) | 1 | 'anon2:child | 'anon2:child: 1->2 |
;
; WHILE FUNCTION RUNS:
; (setf (:child this) child) ; Setting child of Node.1
; -> ['anon3:child: 1->2]
;
; (.set-parent child this)
; (.set-parent <anon4>:this <anon4>:parent
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +------------------------+----+--------------+--------------------+
; | 'anon4:this (Symbol) | 1 | 'anon3:child | 'anon3:child: 2->3 |
; | 'anon4:parent (Symbol) | 1 | 'anon3:this | 'anon3:this : 1->2 |
;
; WHILE FUNCTION RUNS:
; ; note that: (:parent this) => (:parent this) -> (:parent Node.2)
; (setf (:parent this) parent)
; -> ['anon4:parent: 1->2]
; )
; AFTER FUNCTION RUNS:
; -> ['anon4:this: 1->0]
; -> ['anon3:child: 3->2]
; -> ['anon4:parent RC: 2->1]
; )
; AFTER FUNCTION RUNS:
; -> ['anon3:this : 2->1]
; -> ['anon3:child: 2->1]
;
; (setf (:last this) child) ; (:last 'anon2:this) => (:last 'MAIN:firstNode)
; -> ['anon2:child: 2->3]
; )
; AFTER FUNCTION RUNS:
; -> ['anon2:this : 2->1]
; -> ['anon2:child: 3->2]
| Object (type) | RC | Evaluates to |
+--------------------------+----+-----------------+
| 'MAIN:firstNode (Symbol) | 2 | 'Node.1 |
| 'Node.1 (Symbol) | 1 | Node.1 |
| Node.1 (Map) | 1 | <self> |
| 'Node.2 (Symbol) | 1 | Node.2 |
| Node.2 (Map) | 1 | <self> |
| 'anon2:this (Symbol) | 1 | 'MAIN:firstNode |
| 'anon2:child (Symbol) | 2 | 'Node.2 |
| 'anon3:this (Symbol) | 1 | 'anon2:this |
| 'anon3:child (Symbol) | 1 | 'anon2:child |
| 'anon4:parent (Symbol) | 1 | 'anon3:this |
Node.1 = {:child 'anon3:child, :last 'anon2:child}
Node.2 = {:parent 'anon4:parent}
; Before we go overboard and evaluate the entire loop, let's just
; make sure that PARC works in this "easy case". What if we were
; to stop the loop right now and do this:
(setf firstNode nil) ; firstnode, the symbol, now points to nil
; Does PARC work in this "easy case"?
'Node.1 [1->0]
|
+--> Node.1 [1->0]
|
+--> 'anon3:child [1->0]
| |
| +--> 'anon2:child [2->1]
|
+--> 'anon2:child [1->0]
|
+--> 'Node.2 [1->0]
|
+--> Node.2 [1->0]
|
+--> 'anon4:parent [1->0]
|
+--> 'anon3:this [1->0]
|
+--> 'anon2:this [1->0]
|
+--> firstNode [2->1]
; Yup! ^_^
; PARC works just fine so far.
;
; However, we want a more complicated situation with 5 nodes all
; linking to one another in a circle. So let's proceed.
; Let's do another iteration of the loop to find out what the pattern is.
; We should also do it because the logic changes a little bit since after
; the first iteration firstNode will have a :last keyword set.
;
; Remember, it was:
;
; (dotimes (_ 4) ; add 4 children to it
; (.add-child firstNode (Node.))) ; (Node.) returns a SYMBOL!
;
; (.add-child firstNode (Node.)):
; .add-child <anon5>:this <anon5>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-----------------------+----+-----------------+------------------------+
; | 'anon5:this (Symbol) | 1 | 'MAIN:firstNode | 'MAIN:firstNode: 2->3 |
; | 'anon5:child (Symbol) | 1 | 'Node.3 | 'Node.3 : 0->1 |
;
; WHILE FUNCTION RUNS:
; (if-let (last (:last this)) ; 'let1:last = (:last firstNode) = 'anon2:child
; (.set-child last child) ; this gets run!
; (.set-child this child))
;
; ; 'let1:last is another anonymous symbol created by the if-let expression:
;
; | Object (type) | RC | Evaluates to | Has effect |
; +---------------+----+--------------+---------------------+
; | 'let1:last | 1 | 'anon2:child | 'anon2:child [2->3] |
;
; (.set-child <anon6>:this <anon6>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-----------------------+----+--------------+--------------------+
; | 'anon6:this (Symbol) | 1 | 'let1:last | 'let1:last : 1->2 |
; | 'anon6:child (Symbol) | 1 | 'anon5:child | 'anon5:child: 1->2 |
;
; WHILE FUNCTION RUNS:
; (setf (:child this) child) ; Setting child of Node.2
; -> ['anon6:child: 1->2]
;
; (.set-parent child this)
; (.set-parent <anon7>:this <anon7>:parent
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +------------------------+----+--------------+--------------------+
; | 'anon7:this (Symbol) | 1 | 'anon6:child | 'anon6:child: 2->3 |
; | 'anon7:parent (Symbol) | 1 | 'anon6:this | 'anon6:this : 1->2 |
;
; WHILE FUNCTION RUNS:
; (setf (:parent this) parent) ; (:parent this) -> (:parent Node.3)
; -> ['anon7:parent: 1->2]
; )
; AFTER FUNCTION RUNS:
; -> ['anon7:this: 1->0]
; -> ['anon6:child: 3->2]
; -> ['anon7:parent: 2->1]
; )
; AFTER FUNCTION RUNS:
; -> ['anon6:this : 2->1]
; -> ['anon6:child: 2->1]
;
; DON'T FORGET THE SCOPE CREATED BY `if-let'!
; -> ['let1:last: 2->1]
;
; (setf (:last this) child) ; (:last 'anon5:this) => (:last 'MAIN:firstNode)
; -> ['anon5:child: 2->3]
; -> ['anon2:child: 3->2]
;
; AFTER FUNCTION RUNS:
; -> ['anon5:this : 1->0]
; -> ['MAIN:firstNode: 3->2]
; -> ['anon5:child: 3->2]
;
; Thus, after the 2nd iteration, we have the following objects in memory:
| Object (type) | RC | Evaluates to |
+--------------------------+----+-----------------+
| 'MAIN:firstNode (Symbol) | 2 | 'Node.1 |
| 'Node.1 (Symbol) | 1 | Node.1 |
| Node.1 (Map) | 1 | <self> |
| 'Node.2 (Symbol) | 1 | Node.2 |
| Node.2 (Map) | 1 | <self> |
| 'Node.3 (Symbol) | 1 | Node.3 |
| Node.3 (Map) | 1 | <self> |
| 'let1:last (Symbol) | 1 | 'anon2:child |
| 'anon2:this (Symbol) | 1 | 'MAIN:firstNode |
| 'anon2:child (Symbol) | 2 | 'Node.2 |
| 'anon3:this (Symbol) | 1 | 'anon2:this |
| 'anon3:child (Symbol) | 1 | 'anon2:child |
| 'anon4:parent (Symbol) | 1 | 'anon3:this |
| 'anon5:child (Symbol) | 2 | 'Node.3 |
| 'anon6:this (Symbol) | 1 | 'let1:last |
| 'anon6:child (Symbol) | 1 | 'anon5:child |
| 'anon7:parent (Symbol) | 1 | 'anon6:this |
; Notice that 'let1:last was introduced into the table,
; and 'anon5:this disappeared.
;
; Also note that the table above does not contain any keywords
; from the Node maps, and therefore it's not clear why some symbols
; are still around (like 'anon6:child) because it appears nothing is
; referencing them. They are being referenced by the keys in the map.
; In the case of 'anon6:child, it's being referenced by the :child
; key of Node.2. 'anon5:child is being referenced by 'anon6:child
; and the :last keyword of firstNode.
;
; Unfortunately, a clear pattern hasn't established itself yet. There
; has yet to be an obvious repetititive modification of the object table,
; so for proof's sake (so that you're not simply taking my word for it),
; let's go through one more iteration. The last iteration should be
; discernable from the third, because the branching logic is the same
; between the third and the fourth iteration.
;
; Once again, the loop we're iterating is:
;
; (dotimes (_ 4)
; (.add-child firstNode (Node.)))
;
; (.add-child firstNode (Node.)):
; .add-child <anon8>:this <anon8>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-----------------------+----+-----------------+------------------------+
; | 'anon8:this (Symbol) | 1 | 'MAIN:firstNode | 'MAIN:firstNode: 2->3 |
; | 'anon8:child (Symbol) | 1 | 'Node.4 | 'Node.4 : 0->1 |
;
; WHILE FUNCTION RUNS:
; (if-let (last (:last this)) ; 'let2:last = (:last firstNode) = 'anon5:child
; (.set-child last child) ; this gets run!
; (.set-child this child))
;
; ; 'let2:last is another anonymous symbol created by the if-let expression:
;
; | Object (type) | RC | Evaluates to | Has effect |
; +---------------+----+--------------+---------------------+
; | 'let2:last | 1 | 'anon5:child | 'anon5:child [2->3] |
;
; (.set-child <anon9>:this <anon9>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-----------------------+----+--------------+--------------------+
; | 'anon9:this (Symbol) | 1 | 'let2:last | 'let2:last : 1->2 |
; | 'anon9:child (Symbol) | 1 | 'anon8:child | 'anon8:child: 1->2 |
;
; WHILE FUNCTION RUNS:
; (setf (:child this) child) ; Setting child of Node.3
; -> ['anon9:child: 1->2]
;
; (.set-parent child this)
; (.set-parent <anon10>:this <anon10>:parent
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-------------------------+----+--------------+--------------------+
; | 'anon10:this (Symbol) | 1 | 'anon9:child | 'anon9:child: 2->3 |
; | 'anon10:parent (Symbol) | 1 | 'anon9:this | 'anon9:this : 1->2 |
;
; WHILE FUNCTION RUNS:
; (setf (:parent this) parent) ; (:parent this) -> (:parent Node.4)
; -> ['anon10:parent: 1->2]
; )
; AFTER FUNCTION RUNS:
; -> ['anon10:this: 1->0]
; -> ['anon9:child: 3->2]
; -> ['anon10:parent RC: 2->1]
; )
; AFTER FUNCTION RUNS:
; -> ['anon9:this : 2->1]
; -> ['anon9:child: 2->1]
;
; DON'T FORGET THE SCOPE CREATED BY `if-let'!
; -> ['let2:last: 2->1]
;
; (setf (:last this) child) ; (:last 'anon8:this) => (:last 'MAIN:firstNode)
; -> ['anon8:child: 2->3]
; -> ['anon5:child: 3->2]
;
; AFTER FUNCTION RUNS:
; -> ['anon8:this : 1->0]
; -> ['MAIN:firstNode: 3->2]
; -> ['anon8:child: 3->2]
;
; Thus, after the 3rd iteration, we have the following objects in memory:
| Object (type) | RC | Evaluates to |
+--------------------------+----+-----------------+
| 'MAIN:firstNode (Symbol) | 2 | 'Node.1 |
| 'Node.1 (Symbol) | 1 | Node.1 |
| Node.1 (Map) | 1 | <self> |
| 'Node.2 (Symbol) | 1 | Node.2 |
| Node.2 (Map) | 1 | <self> |
| 'Node.3 (Symbol) | 1 | Node.3 |
| Node.3 (Map) | 1 | <self> |
| 'Node.4 (Symbol) | 1 | Node.4 |
| Node.4 (Map) | 1 | <self> |
| 'let1:last (Symbol) | 1 | 'anon2:child |
| 'let2:last (Symbol) | 1 | 'anon5:child |
| 'anon2:this (Symbol) | 1 | 'MAIN:firstNode |
| 'anon2:child (Symbol) | 2 | 'Node.2 |
| 'anon3:this (Symbol) | 1 | 'anon2:this |
| 'anon3:child (Symbol) | 1 | 'anon2:child |
| 'anon4:parent (Symbol) | 1 | 'anon3:this |
| 'anon5:child (Symbol) | 2 | 'Node.3 |
| 'anon6:this (Symbol) | 1 | 'let1:last |
| 'anon6:child (Symbol) | 1 | 'anon5:child |
| 'anon7:parent (Symbol) | 1 | 'anon6:this |
| 'anon8:child (Symbol) | 2 | 'Node.4 |
| 'anon9:this (Symbol) | 1 | 'let2:last |
| 'anon9:child (Symbol) | 1 | 'anon8:child |
| 'anon10:parent (Symbol) | 1 | 'anon9:this |
; Now a clear pattern has emerged. For any subsequent iterations
; we simply:
;
; 1. Add 'Node.X and Node.X (where X is the iteration after 4)
; 2. Add 'letY:last (where Y is the iteration after 2) and point it
; at anon[Z+3]:child, where Z was the context number of the previous symbol
; (which was the most recent value of (:last firstNode) before we ran
; the iteration.
; 3. Add four extra symbols to the end of the table (following
; the self-evident pattern).
;
; Thus, after the fourth iteration of the loop we get the following Object+RC table:
| Object (type) | RC | Evaluates to |
+--------------------------+----+-----------------+
| 'MAIN:firstNode (Symbol) | 2 | 'Node.1 |
| 'Node.1 (Symbol) | 1 | Node.1 |
| Node.1 (Map) | 1 | <self> |
| 'Node.2 (Symbol) | 1 | Node.2 |
| Node.2 (Map) | 1 | <self> |
| 'Node.3 (Symbol) | 1 | Node.3 |
| Node.3 (Map) | 1 | <self> |
| 'Node.4 (Symbol) | 1 | Node.4 |
| Node.4 (Map) | 1 | <self> |
| 'Node.5 (Symbol) | 1 | Node.5 |
| Node.5 (Map) | 1 | <self> |
| 'let1:last (Symbol) | 1 | 'anon2:child |
| 'let2:last (Symbol) | 1 | 'anon5:child |
| 'let3:last (Symbol) | 1 | 'anon8:child |
| 'anon2:this (Symbol) | 1 | 'MAIN:firstNode |
| 'anon2:child (Symbol) | 2 | 'Node.2 |
| 'anon3:this (Symbol) | 1 | 'anon2:this |
| 'anon3:child (Symbol) | 1 | 'anon2:child |
| 'anon4:parent (Symbol) | 1 | 'anon3:this |
| 'anon5:child (Symbol) | 2 | 'Node.3 |
| 'anon6:this (Symbol) | 1 | 'let1:last |
| 'anon6:child (Symbol) | 1 | 'anon5:child |
| 'anon7:parent (Symbol) | 1 | 'anon6:this |
| 'anon8:child (Symbol) | 2 | 'Node.4 |
| 'anon9:this (Symbol) | 1 | 'let2:last |
| 'anon9:child (Symbol) | 1 | 'anon8:child |
| 'anon10:parent (Symbol) | 1 | 'anon9:this |
| 'anon11:child (Symbol) | 2 | 'Node.5 |
| 'anon12:this (Symbol) | 1 | 'let3:last |
| 'anon12:child (Symbol) | 1 | 'anon11:child |
| 'anon13:parent (Symbol) | 1 | 'anon12:this |
; create a loop by having the child of the last node be the first node itself
(.set-child (:last firstNode) firstNode)
; To evaluate this we must know the value of firstNode before we call
; .set-child above. What is the value of firstNode then?
;
; Node.1 = {:child 'anon3:child :last 'anon11:child}
;
; Now, let's break it down:
;
; (.set-child <anon14>:this <anon14>:child
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +------------------------+----+-----------------+-----------------------+
; | 'anon14:this (Symbol) | 1 | 'anon11:child | 'anon11:child : 2->3 |
; | 'anon14:child (Symbol) | 1 | 'MAIN:firstNode | 'MAIN:firstNode: 2->3 |
;
; WHILE FUNCTION RUNS:
; (setf (:child this) child) ; (:child this) => (:child Node.5)
; -> ['anon14:child: 1->2]
;
; (.set-parent child this)
; (.set-parent <anon15>:this <anon15>:parent
; BEFORE FUNCTION RUNS:
; | Object (type) | RC | Evaluates to | Has effect |
; +-------------------------+----+---------------+---------------------+
; | 'anon15:this (Symbol) | 1 | 'anon14:child | 'anon14:child: 2->3 |
; | 'anon15:parent (Symbol) | 1 | 'anon14:this | 'anon14:this : 1->2 |
;
; WHILE FUNCTION RUNS:
; (setf (:parent this) parent) ; (:parent firstNode) = 'anon15:parent
; -> ['anon15:parent: 1->2]
; )
; AFTER FUNCTION RUNS:
; -> ['anon15:this: 1->0]
; -> ['anon14:child: 3->2]
; -> ['anon15:parent: 2->1]
; )
; AFTER FUNCTION RUNS:
; -> ['anon14:this : 2->1]
; -> ['anon14:child: 2->1]
;
; Here's the final table we're left with:
| Object (type) | RC | Evaluates to |
+--------------------------+----+-----------------+
| 'MAIN:firstNode (Symbol) | 3 | 'Node.1 |
| 'Node.1 (Symbol) | 1 | Node.1 |
| Node.1 (Map) | 1 | <self> |
| 'Node.2 (Symbol) | 1 | Node.2 |
| Node.2 (Map) | 1 | <self> |
| 'Node.3 (Symbol) | 1 | Node.3 |
| Node.3 (Map) | 1 | <self> |
| 'Node.4 (Symbol) | 1 | Node.4 |
| Node.4 (Map) | 1 | <self> |
| 'Node.5 (Symbol) | 1 | Node.5 |
| Node.5 (Map) | 1 | <self> |
| 'let1:last (Symbol) | 1 | 'anon2:child |
| 'let2:last (Symbol) | 1 | 'anon5:child |
| 'let3:last (Symbol) | 1 | 'anon8:child |
| 'anon2:this (Symbol) | 1 | 'MAIN:firstNode |
| 'anon2:child (Symbol) | 2 | 'Node.2 |
| 'anon3:this (Symbol) | 1 | 'anon2:this |
| 'anon3:child (Symbol) | 1 | 'anon2:child |
| 'anon4:parent (Symbol) | 1 | 'anon3:this |
| 'anon5:child (Symbol) | 2 | 'Node.3 |
| 'anon6:this (Symbol) | 1 | 'let1:last |
| 'anon6:child (Symbol) | 1 | 'anon5:child |
| 'anon7:parent (Symbol) | 1 | 'anon6:this |
| 'anon8:child (Symbol) | 2 | 'Node.4 |
| 'anon9:this (Symbol) | 1 | 'let2:last |
| 'anon9:child (Symbol) | 1 | 'anon8:child |
| 'anon10:parent (Symbol) | 1 | 'anon9:this |
| 'anon11:child (Symbol) | 3 | 'Node.5 |
| 'anon12:this (Symbol) | 1 | 'let3:last |
| 'anon12:child (Symbol) | 1 | 'anon11:child |
| 'anon13:parent (Symbol) | 1 | 'anon12:this |
| 'anon14:this (Symbol) | 1 | 'anon11:child |
| 'anon14:child (Symbol) | 1 | 'MAIN:firstNode |
| 'anon15:parent (Symbol) | 1 | 'anon14:this |
Node.1 = {:child 'anon3:child, :last 'anon11:child, :parent 'anon15:parent}
Node.2 = {:parent 'anon4:parent :child 'anon6:child}
Node.3 = {:parent 'anon7:parent :child 'anon9:child}
Node.4 = {:parent 'anon10:parent :child 'anon12:child}
Node.5 = {:parent 'anon13:parent :child 'anon14:child}
; Drum roll!! Now is the moment of truth. If, after the next line completes,
; all we're left with is just the symbol 'MAIN:firstNode,
; then Perfect Automatic Reference Counting works perfectly!
(setf firstNode nil) ; firstnode, the symbol, now points to nil
'Node.1 [1->0]
|
+--> Node.1 [1->0]
|
+--> 'anon3:child [1->0]
| |
| +--> 'anon2:child [2->1] ; referenced by (:parent Node.3)
|
+--> 'anon11:child [3->2]
|
+--> 'anon15:parent [1->0]
|
+--> 'anon14:this [1->0]
|
+--> 'anon11:child [2->1] ; referenced by Node.4, etc.
; FAIL! anon11:child , 'anon2:child, 'let1:last
;
; Two cycles observed:
;
; 1. anon11:child is referenced by itself through Node.4
; 2. anon2:child is referenced by itself through Node.2 via anon6:child
; anon2:child
; |
; let1:last
; ^
; |
; 'anon5:child
; |
; 'anon6:child
; |
; 'Node.2
; |
; 'anon2:child
;
; anon2:child is also referenced by anon11:child...
;
;
; 'anon11:child has the following 3 references:
; 1. anon12:child
; 1. (:child Node.4)
; 1. 'Node.4
; 1. 'anon8:child
; 1. 'let3:last
; 1. 'anon12:this
; 1. 'anon13:parent
; 1. (:parent Node.5) <- 'Node.5
; 1. 'anon11:child !!!! CYCLE !!!
;
; 2. (:last Node.1) -> ['anon11:child 3->2] GOT RID OF THIS
; 3. anon14:this [1->0]: ['anon11:child 2->1] GOT RID OF THIS
; 1. anon15:parent [1->0]
;
; 'anon2:child has:
; 1. anon3:child: GOT RID OF THIS via '[anon2:child 2->1]
; 2. let1:last
; 1. anon6:this
; 1. anon7:parent
; 1. (:parent Node.3) <- 'Node.3
; 1. 'Node.3
; 1. 'anon5:child
; 1. 'let2:last
; 1. 'anon9:this
; 1. 'anon10:parent
; 1. (:parent Node.4) <- 'Node.4
; 1. 'anon8:child
; 1. 'let3:last
; 1. 'anon12:this
; 1. 'anon13:parent
; 1. (:parent Node.5) <- 'Node.5
; 1. 'anon11:child !!!! CYCLE !!!
; 2. 'anon9:child
; 2. 'anon6:child
; 1. (:child Node.2)
; 1. 'Node.2
; 1. anon2:child !!! CYCLE !!!
@taoeffect

This comment has been minimized.

Copy link
Owner Author

taoeffect commented Oct 5, 2013

This is an updated version of an original attempt at PARC posted online in 2009: http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=15&t=3151

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.