There seem to be a number of macros that could be replaced by plain functions except for some small cosmetic difference. For instance, the while macro could be rewritten as a higher-order function and used as follows: (while [blah]
[blah blah blah])
The problem is that the bracket functions expect an argument, which doesn't make sense in this case. We could make the argument optional, but then _ would get bound to nil, shadowing any existing binding.The alternative is to write (while (fn () (blah))
(fn () (blah blah blah)))
which is just ugly. And so we make while a macro instead of a function.And this depresses me, because macros, as we all know, are more troublesome than HOFs, for a million reasons: they're hard to read, they're error-prone, you have to fiddle with gensyms, you can't use nice things like afn and aif because of variable capture, etc. etc. Here's my idea: why not define things like while as HOFs, and have the language generate a macro that handles the cosmetic issues for us? Example: (defho while ([test ()] . [f ()])
((afn ()
(when (test)
(f)
(self)))))
The parameter list says to wrap the first form in an (fn () ...), and all the remaining forms in another (fn () ...). So supposing you wrote (while (blah)
(blah blah blah))
this would expand to (while-aux (fn () (blah))
(fn () (blah blah blah)))
where while-aux is a function generated by defho.Here's another example, a drop-in replacement for awhen: (defho awhen (x . [f (it)])
(if x (f x)))
This system breaks down for macros that ask the caller for the name of the variable to capture (e.g. for). To resolve this, we might have syntax to denote parameters that shouldn't be evaluated, and more syntax to denote where the contents of a variable should be included in the macroexpansion. Here's an example (overloading the asterisk for both purposes): (defho for (*v i max . [f (*v)])
(while (<= i max)
(f i)
(++ i)))
Obviously, this idea won't eliminate macros entirely, but I suspect it would greatly reduce the need for them--perhaps even the majority of macros could be replaced by defhos.Here are a few more examples: (defho loop (start [test ()] [update ()] . [f ()])
(while (test)
(f)
(update)))
(defho whilet (*v [test ()] . [f (*v)])
((afn ()
(awhen (test)
(f it)
(self)))))
(defho accum (*v . [f (*v)])
(let xs nil
(f [push _ xs])
xs))
I suggest you compare these definitions with those currently in arc.arc. The difference is striking.Of course this is all speculative and I haven't bothered to implement it, so there may be some rough edges that I haven't thought about yet. Do you guys think this is a good idea? Has anyone done something similar before? |