The argument that is frequently brought up against reference counting is that it can result in complicated code, and memory leaks through cyclical referencing (AKA retain cycles). I think that both of these two arguments can be avoided if reference counting is implemented in a certain way.
Remember, newLISP's ORO, while great in many ways, is slower than incrementing/decrementing integers (which is what reference counting is) because it results in a lot of copying of data, and in turn in a lot of allocation and deallocation of data. It also means that it's hard to do certain things in newLISP, like implement complicated object-oriented structures and relationships.
But first let's make sure we understand what the argument is against reference counting.
Circular references, or retain cycles, occur in other languages like so:
1) I have a reference to a "Car" object.
Code: Select all
Car a = new Car(); // 'a' has a retain count of 1
Code: Select all
public Car() {
this.filter = new Filter(this); // 'this' car has retains 'filter'
}
Code: Select all
Car a = new Car(); // 'a' has retain count of 2!
release(a); // 'a' is not destroyed!
Is there a way to fix this? Let's try:
Code: Select all
public Car() {
this.filter = new Filter(this); // 'this' car has retains 'filter'
release(this); // we're back down to 1
}
Code: Select all
Car a = new Car(); // this time, 'a' does have a retain count of 1
release(a); // *car crash!*
----------------------------------------------------------------------------------
So, is an alternative possible? I believe I can implement this exact scenario in a special dialect of newLISP that uses reference counting in:
1) A clean and efficient way.
2) Without worrying about retain cycles.
How this would work is shown in an example program below that does the same thing with the Car and the Filter, but without the retain cycle. Please remember that this is *not* newLISP code, but something similar:
Code: Select all
; while reading this, you may wish to simultaneously read the first couple of comments
; down below after the function definitions.
(define (Car:Car this , filter)
; in this function, the symbols 'this' and 'filter' are not associated with the 'Car'
; context, but with an anonymous context created for each invocation of this function.
; The anonymous context disappears when all of its symbols are deallocated.
(set 'filter (Filter 'filter this))
(list (context) this filter)
; at the end of this function imagine: (delete 'filter 'this) which results in a decrement
; to the reference/retain count (RC) associated with the *symbols* "this" and "filter"
; which, again, exist in an anonymous context. When the function exists, their RC of "this" will
; be 2, because it's retained by the list and by the symbol in the list returned by Filter.
; The RC of the symbol "filter" will be 1 because it's only retained by the "this" symbol in
; the list returned by the call to Filter. The last thing in the list returned by this function is
; a *list* returned by the function Filter. Its retain count will be 1 only, because only the
; list here retains it. Notice that the list's RC is 1 and not 2 as you might expect. That is
; because when a list is created its RC is "in limbo". It's really 0, but it's held for a short
; period after the list is created. If it isn't immediately assigned to another symbol it will be
; deallocated. This equally applies to the list returned by this function.
)
(define (Filter:Filter this car)
; here again "this" and "car" are symbols in a different anonymous context created when this function
; is called. they start out with a retain count of 1 and they retain their values! (other symbols passed in).
(list (context) this car)
; they are retained when they are passed into the list (RC:2). And then they are released when the
; function exits (RC:1)
)
(define (Car:filter car)
; here again, "car" is a different symbol from the one passed in, but it "points to" the same value
; as whatever symbol that's passed in. That value is the list from Car:Car!
(car 2)
; implicit indexing fetches the value from the symbol (which should point to a list) and then
; performs indexing on that *value*!
)
(define (Filter:car filter)
(filter 2)
)
(set 'car (Car 'car))
; => (Car <anonCtx2343>:this <anonCtx2343>:filter)
; Note! At this point MAIN:car's retain count is 2! It had an initial retain count for being brought into existence
; as a symbol, and then <anonCtx2343>:this retained it as well! BUT! The value pointed to by the symbol, the list, its
; RC is 1! Because the only thing holding onto it is the symbol MAIN:car!
(:filter car)
; => <anonCtx2343>:filter
(eval (:filter car))
; => (Filter <anonCtx223>:this <anonCtx223>:car)
; now we do what the other languages can't!
(set 'car nil)
; it's important to note that this call is *different* from what happens at the end of a function call! It is
; *not* equivalent to (delete 'car). The symbol MAIN:car still exists after (set 'car nil) is done.
; What happens instead is that the *value* pointed to by the symbol is fetched, and *its* retain count
; is decremented. That value was the list created by Car:Car, which only had a retain count of 1
; (because it was retained by MAIN:car). So the list is deallocated, and in the process it goes through
; and releases everything inside of it. Let's ignore the first thing in the list (the context Car)
; The next thing is <anonCtx2343>:this. The *symbol*'s RC is decremented, it is now 1. Next is actually
; not a symbol but a list! Its RC is 1! It's decremented! It's 0! As it is destroyed, everything inside of
; it is released too! So <anonCtx223>:this is released and destroyed! What it pointed to (the symbol
; <anonCtx2343>:filter), is released as well and it *too* is destroyed because its RC was only 1!
; Finally, <anonCtx223>:car, which only has an RC of 1 is released, and in its destruction the final
; symbol to survive, <anonCtx2343>:this, is destroyed as well!
; Memory Managed!
Code: Select all
(define (Car:Car this , filter)
(set 'filter (Filter 'filter this))
(list (context) this filter)
)
(define (Filter:Filter this car)
(list (context) this car)
)
(define (Car:filter car)
(car 2)
)
(define (Filter:car filter)
(filter 2)
)
(set 'car (Car 'car))
(:filter car)
(eval (:filter car))
(set 'car nil)
Thoughts?