Oh, I keep forgetting: I'm not sure I like having '+ be implemented using just a two-argument version. Naively concatenating two things at a time is embarrassingly inefficient (http://en.wikipedia.org/wiki/Schlemiel_the_Painter%27s_algor...). So, I'm sorry I suggested it!
Instead of that approach, I've been thinking about having one function that wraps the first argument in some kind of output stream, one function that submits a new argument to that stream, and one function that gets the value accumulated in that stream. So here's yet another untested, throwaway code example illustrating the functionality I'm talking about (but not the extensibility, unless you use 'extend):
(= orig-inside inside orig-+ +)
(def +streamer (x)
(case type.x string (w/outstring str (disp x str) str)
int x
num x
(if alist.x
(annotate 'basic-streamer
(obj test alist func [apply join _] args list.x))
(err "unrecognized case"))))
(def fn-add-to (segment streamer)
(if (and (isa streamer 'output) (isa segment 'string))
(do (disp segment streamer)
streamer)
(and (in type.streamer 'int 'num) (in type.segment 'int 'num))
(orig-+ streamer segment)
(and (isa streamer 'basic-streamer) rep.streamer!test.segment)
(do (push segment rep.streamer!args)
streamer)
(err "unrecognized case")))
(mac add-to (segment place)
(w/uniq g-streamer
`(zap (fn (,g-streamer)
(fn-add-to ,segment ,g-streamer))
,place)))
(def inside (x)
(case type.x output orig-inside.x
basic-streamer (rep.x!func rep.x!args)
int x
num x
(err "unrecognized case")))
(def + args
(iflet (first . rest) args
(let s +streamer.first
(each arg rest
(add-to arg s))
inside.s)
0))
Inefficient concatenation is probably what's bogging down Penknife's parser, so I'll put this technique to the test sooner or later to see how much it helps.
I haven't found my way into cons-counting.... I know it's probably sloppy, but I allocate as though it takes constant time. Given that assumption, I'm less concerned with an overall constant-time slowdown and more concerned with allowing a '+ of N sequences of length L to take O(NL) time rather than O(N^2 L) time.
This is also a mostly[1] less limited design from an interface point of view. It's straightforward to port an extension from my other design to this one: The '+streamer and 'inside functions will be extended with (fn (x) x), and 'fn-add-to will be extended with the desired two-argument behavior. (I'd personally make a macro for this, and I'd use it to (re)implement the number-and-number case.)
[1] The exception is if you want to use '+ on streamer types themselves, since a '+streamer behavior of (fn (x) x) will cause that type to be confused with whatever other type wraps itself up as that streamer type.