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?