Do you plan to include support for functions that accept arbitrary keywords? How about a version of 'apply that can pass keywords too? These are useful for functions that just want to pass most of their arguments along, as in this extreme example:
; Build a function whose behavior imitates the behavior of
; calling a nonconstant expression, such as a potentially
; hackable global variable.
(def fn-late (body)
(fn args (apply (do.body) args)))
(mac late body
`(fn-late:fn () ,@body))
(= first car)
(= rest cdr)
(= past rev:rest:rev)
(= last first:rev)
If someone redefines 'car and 'cdr to work with strings, 'first and 'rest won't reflect the change. If someone redefines 'first and 'rest, I think 'past and 'last won't reflect those changes either (but I'm not sure right now). They're not hackable. Here are two ways to make them hackable again:
(= first [car _])
(= rest [cdr _])
(= past [rev:rest:rev _])
(= last [first:rev _])
(= first late.car)
(= rest late.cdr)
(= past late.rev:late.rest:late.rev)
(= last late.first:late.rev)
I prefer the first way, but you never know. ^_^
Now suppose you want to use 'late to make a function that delegates to a function that takes keyword arguments. Will the technique 'late uses, [fn args (apply _ args)], still be aufficient? I suspect it would need to change to [fn (?. keys . rest) (apply _ :keys keys rest)] or something, and I was wondering if you already had a plan for those extra features.
By the way, macros complicate this issue. If a macro is given keywords it doesn't recognize, are they just passed to it as symbols in its rest arg, or should it be treated the same way as a function?
A pattern is starting to emerge. You're thinking about redefinition much harder than me. My approach so far has been: compiler warns of redefinition, I stop the presses and go figure out if it's a problem.
"If a macro is given keywords it doesn't recognize, are they just passed to it as symbols in its rest arg, or should it be treated the same way as a function?"
I hadn't considered that, but fwiw the current wart implementation simply strips out unrecognized keyword args and the values provided for them. Hmm, that's probably wrong.
My only point with 'late is to demonstrate a utility that needs a different implementation once keyword arguments are introduced to the language, and which may have no implementation at all if the keyword argument framework isn't comprehensive enough.
But yeah, I do think about redefinition a lot. ^^ Penknife's core library, when I get around to it, will be designed for people to be able to modify it, the way people do with arc.arc. I'm also trying to give it a module system that plays nicely with that kind of invasive coding style. It's for customizability's sake.
If someone redefines 'car and 'cdr to work with strings, 'first and 'rest won't reflect the change.. They're not hackable.
It's interesting; there seems to be a tension between future hackability and <strike>verbosity</strike> brevity. Perhaps the best way to get the best of both worlds is to go for an even more dynamic interpreter. Like forth or factor, just have all name lookups happen at runtime.
Do you mean that if you say (= foo (a b c)), then (a b c) shouldn't be evaluated until you look up foo? Well, I know I wouldn't want that all the time:
(= setter (table)) ; oops ^_^
It's interesting; there seems to be a tension between future hackability and [brevity].
I think that's only a tension between being brief and being specific. It can't be avoided; if I specifically want things to be a certain way (e.g. hackable), I'm willing to leave the beaten path to get there. The real issue for me is how far off the beaten path I can get while still being brief enough not to get fed up and make a more hospitable language. :-p
There's another kind of brevity tension. I think a language that tries to maximize brevity doesn't need to worry about having a small implementation too. After all, it's probably built in a more verbose language. ...But now that's really off-topic. XD
I was going to call that '=late. ^_^ There are still other cases where a global function can end up stored somewhere it doesn't stay up-to-date with the variable binding, like storing it as a behavior in a data sructure or using (compare ...). But I don't expect to need anything more brief than 'late, actually.
It's funny, I hadn't settled on the name 'late until I posted it here, and before that point I was thinking of it as 'alias. ^_^ I wanted to avoid confusion with Racket's aliases, so I changed it at the last second.
Macros generally aren't hackable anyway, right? I don't have any ideas to change that.... Well, except these I guess. :-p
a) Change the whole language over to fexprs. This is probably the most elegant way to make things hackable, but it'll probably be inefficient without a convoluted partial evaluation infrastructure (ensuring things are free of side effects, and headaches like that ^_^ ).
b) Record which macros are expanded every time a command is evaluated, and re-run commands whenever their macros change. Running commands out of order and multiple times would be pretty confusing. (I bet there'd be ways to manage it, but I for one would forget to use them at the REPL.) It would also be easy to fall into an infinite loop by trying to redefine a macro using a command that actually uses that macro.
first:rev is already resistant to changes in first or rev
Oh, good to know. ^_^ For Penknife, I tried to make [= foo a:b] produce a custom syntax if a or b was a custom syntax, but I got stuck. I ended up with [foo ...] using the local values of a and b, I think. In the process I got a bit confused about how a:b was supposed to work in the edge cases, from a design point of view.
This experiment was reflected in two commits in the Git repo: One introducing it, and one removing it 'cause of the poorly thought-out complexity it introduced. :)