The point is that it makes a large class of macros easier to define. Really, compare my examples with the definitions in arc.arc; the former are much shorter and easier to understand than the latter.
My examples are from the core language, obviously, but the suggestion would benefit anyone wanting to extend the language with their own macros.
Placing a parameter inside brackets just says that the argument should be expanded into an fn. The thing after the parameter name is the parameter list that the fn should have. As to the asterisk, I don't know how to explain that better than I already have.
Well, it would be a good addition to Anarki if you can actually implement it, and can prove your point that it's easier to use than using macros - obviously to do that, you can't just rewrite existing code, you have to write new code that uses your version of macros and see if it does indeed work better.
Further, using arc.arc as a basis is pointless; the reason those macros are there is because it's a waste of time to rewrite them. Most of the macros I've written in Arc depend on new syntax, not just rearranging expressions: look at p-m: and w/html , which I doubt are possible in ordinary HOF style.
So yes: while it certainly looks interesting, it doesn't seem to leverage the "common enough" theme quite enough.
Also, I somehow feel that what you really want are hygienic macros, which would look much more similarly to what you're doing.
Yes, hygienic macros could work too, provided we devise a good way to hack anaphora onto them. Another possible solution would be to just have a very concise syntax for function literals. Either way, I think that the rampant use of (unhygienic) macros where they are not necessary is a problem and needs to be addressed somehow. Especially since Arc is a Lisp-1.
Maybe you'd like them more if you called them "macros" instead of "unhygienic macros" ;)
No, really, macros, being essentially compilers, give you enough power to build everything you'd ever want into Lisp, including "hygienic" macros, and even to build facilities to write hof-like things in less painful ways.
Maybe they're a pain in the ass if you don't "go meta", in the same way computers are a pain in the ass if you don't build OSes and compilers first.
The real point in favor of unhygienic macros is that they are less constraining and, personally, I find them easier to write and read than hygienic macros. I don't find a bad idea to have both hygienic macros and unhygienic macros.
Obviously I like unhygienic macros when they are necessary. The problem is, I keep writing higher-order functions and getting tired of typing the "fn" over and over, and then I have to convert the function to a macro, making it twice as long and hard to read. Hygienic macros help, but they still are not as easy to write as plain functions.
I know I'm not the only person who has this problem, but maybe I'm the only one who realizes I have this problem. I see people on the Internet raving about the AMAZING POWER of macros, and most of their examples are just higher-order functions with some small cosmetic changes. Most of the macros in Arc are of the same kind.
I'm not denying that macros are powerful. I just think there is a gross inefficiency in using them where you shouldn't have to, just because of minor syntactic concerns.
I wanted to solve this with a short, clean syntax for function literals, but I haven't been able to come up with one and neither, apparently, has anyone else. So instead I decided to try something that would generate macros out of HOFs.
I thought this would be evident from my post, but apparently it wasn't.
Maybe we could use { args | body } ? I don't think the braces are taken.
Now, maybe that's a bad idea; instead, we could redefine the brackets, so that a pipe divides args and body, and if it doesn't have a pipe, it defaults to binding _ ? I don't know how hard that would be, or some variation on the concept, but it would be a bit shorter than (fn (args) (body)), if you don't want to type that all the time.
And how exactly does 'w/hof work, as defined so far? And if it's "standard" now, why not just implement it?
I wondered if redefining [...] might be possible. It seems to me that the new double bar syntax is practically a super-set of the old one: if it doesn't have the double bar, just treat it like the old bracket function and define _ to be the argument; otherwise use the argument list provided. Including an empty list, I would hope.
I think it's a bad choice, personally. I'm not crazy about the single pipe either, but || is awful.
Tangent: this may be a dumb question, but do we really need the pipe character for symbols? I know I've never used it. Why not disallow spaces (and the like) in symbols, and free the pipe for new syntax?
[ a b c :
(/ (+ (- b) (sqrt (+ (* b b) (* 4 a c))))
(* 2 a))]
[: (thunk this)]
[ a b c ->
(/ (+ (- b) (sqrt (+ (* b b) (* 4 a c))))
(* 2 a))]
[-> (thunk this)]
[ a b c =>
(/ (+ (- b) (sqrt (+ (* b b) (* 4 a c))))
(* 2 a))]
[=> (thunk this)]
Nesting doesn't seem impossible: the reader, I think, will handle nesting as:
[foo [bar]]
(make-br-fn (foo (make-br-fn (bar))))
As for implementation, it's easy:
(given ; this gives us access to the old
; implementation of [] syntax; it
; is used when we don't find the
; separator
old (rep make-br-fn)
; use a variable to easily change
; the separator
separator ': ;for experimentation
(= make-br-fn
; a macro is just a function that has
; been tagged (or annotated) with the
; symbol 'mac
(annotate 'mac
; the reader reads [...] as
; (make-br-fn (...))
(fn (rest)
; find the separator
(if (some separator rest)
; note the use of the s-variant givens
; the "s" at the end of the name of givens
; means that the variables are specifically
; bound in order, and that succeeding variables
; may refer to earlier ones
(givens ; scans through the list, returning
; an index for use with split
; (no built-in function does this)
scan
(fn (l)
((afn (l i)
(if (caris l separator)
i
(self (cdr l) (+ i 1))))
l 0))
; now do the scan
i (scan rest)
; this part destructures a two-element
; list
(params body)
; used to get around a bug in split
(if (isnt i 0)
(split rest i)
(list nil rest))
; it just becomes an ordinary function
; body includes the separator,
; so we also cut it out
`(fn ,params ,@(cut body 1)))
; pass it to the old version of make-br-fn
; if a separator was not found
(old rest))))))
Edit: tested. Also reveals a bug in split: (split valid_list 0) == (split valid_list 1)
(= foo [ i :
[ : i]])
((foo 42))
edit2: p.s. probably not really easy much after all^^. As a suggestion, (help "stuff") is good at finding stuff.
> Tangent: this may be a dumb question, but do we really need the pipe character for symbols? I know I've never used it. Why not disallow spaces (and the like) in symbols, and free the pipe for new syntax?