Arc suports dynamically redefining functions by using something like the following pattern: (let old car
(= foo
(fn (c)
(prn "car!")
(old c))))
This pattern has been captured in the Anarki 'redef: (redef car (c)
(prn "car!")
(old c))
It's a simple and direct way of overloading a function, for example to add handling for, say, your own types. This style is known as "chaining", since, if you can't handle the case, you fall through to the "old" version.Unfortunately, this informal method of overloading can be badly handled. The cases are edgy, but the last thing you want in a language is to have to handle edge cases. For example, consider a vector type implemented underneath as a table: (def vector ()
(annotate 'vector (table)))
(defcall vector (v i)
(if (and (isa i 'int) (>= i 0))
(v i)
(err:string "Vector expects a non-negative integer - " i))
(redef sref (v val i)
(if (isa v 'vector)
(sref (rep v) val i)
(old v val i)))
(redef len (v)
(if (isa v 'vector)
(+ 1 (or (best > (keys:rep v)) -1))
(old v)))
(redef keys (v)
(if (isa v 'vector)
(let max (best > (keys:rep v))
(if max (range 0 max)))
(old v)))
For convenience, you can also redefine 'cut: (redef cut (v start (o end (len v)))
(if (isa v 'vector)
(let newv (vector)
(if (< end 0) (zap + end (len v)))
(if (< start 0) (zap + start (len v)))
(for i start (- end 1)
(= (newv (- i start)) (v i)))
newv)
(old v start end)))
It'll work, right?Unless you happen to have done this before you loaded the above: (require "lib/scanner.arc")
Why? Because scanners can be infinite: (def generator (f init)
(scanner 'car init
'cdr (generator f (f init))))
scanner.arc's 'cut, in fact, will work properly with infinite lazy lists like the above: (= foo (generator [+ 1 _] 0))
(= bar (cut foo 2))
(car bar)
=> 2
Unless, that is, you redef cut as in the 'vector example. Then it'll fail, because vector's 'cut attempts to get the length of an infinite list!Of course, nobody uses scanners anyway. YET. But what if someone does? Then we suddenly get all sorts of very strange behavior when we start mixing libraries: ouch. Or take another example. Suppose our 'redef of a function is buggy: (redef cut (seq start (o end (lne esq)))
(...))
The problem is that we can't easily remove it: the bug is now embedded into the system, because of chaining. When you correct this and attempt to reload it, it will still fail, because instead of replacing the handling, we're just chaining: and everything after the buggy link is now affected by the bug.This is one reason why we need a better multimethod dispatch scheme. |