That is certainly the arc way to do it, but there's one thing I'm not sure about: do you want mac:let or let:mac? Or, in fact, would either one work? The problem I'm trying to get at is what happens to let's three arguments (symbol, value, body) when you compose mac with it. It seems like you really want mac to apply to only the value argument, which is not a straight composition.
I almost hate to get into this, but it seems to me that the real solution is first-class macros:
(let my-macro (mac (x) `(prn ,x))
...)
I believe pg said that one of the things he gave up in the current implementation that he'd really like to have is first-class macros, and it may be that we can't get them in mzscheme without unacceptable performance loss, so perhaps maclet is the temporary solution, but this seems like the long-term way to go.
arc> (let m (mc (x) `',x) (m 'not-a-declared-symbol))
(quote not-a-declared-symbol)
. However, here I'm not sure why. So after working through this, I'm not actually sure what these results signify. But here they are. Make of them what you will.
I'm not exactly sure if this is what you want. I believe there is a difference between macros that are only expanded once (in lazy compilation), and macros that are called each time the macro-expression is encountered. Though the latter case would only make sense if the macro exhibits side-effects, or depends on side-effects, or if the argument list to the macro may change. Not sure whether there is any benefit in that.
first-class macros would obviously solve this problem completely, that same way scheme eliminates the need for CL's flet and labels. it would be nice if we could just assign anonymous macros to bindings, but we can't do that yet.
the problem is that the composition just doesn't really make sense in this context, as you said. it doesn't translate the way composition should. in my opinion, maclet is deserving of it's own form.
i'm not certain whether this would be abuse of function composition or not. for some reason, maclet and macwith just seem more appropriate than mac:let and mac:with. also seems sort of dangerous to override such an important construct.
My concern is largely namespace pollution. By overloading (NOT overriding) 'mac, we squeeze this feature into a smaller namespace, adding 0 new macros ^^. In any case, the arc-wiki 'breakable and 'p-m macros have a similar intended use too ^^.
It does seem like you're going to want to do a lot of macro composition where you only compose over the body argument. Is it possible to make this work? The most obvious way for me is to use the single-argument function syntax, but somehow modified for macros, like this:
([let x 3 _]:pm:body)
This is just a really quick thought. Any comments?
They're different because they don't actually exist. Observe:
arc> def
#3(tagged mac #<procedure>)
arc> fn
Error: "reference to undefined identifier: _fn"
Since def is a macro, we can use it anywhere we like. But since fn is not, we can only use it in the head of a list, and it can't be overridden. fn, if, etc. should be first class: if not macros, then (type fn) should be 'prim or some such thing.
Oh, it is not just CLOS. defstruct also defines setters and many an apparent reader function is also a writer, (so we call them accessors). That means one does not fire up (defun (setf foo)...) unless there is an usual case of abstraction where there is some function on an object (not just reading a slot) and where there is some state change intelligibly viewed as being transitive aka share the name with the reader.
In re writing a defun, I probably just talked you out of it, but here is what I did (warning, old nooby Arc code) for optional and keyword args:
(mac defun (name params . body)
(w/uniq (rtargs)
`(def ,name ,rtargs
(withs ,(with (reqs nil key? nil opt? nil keys nil opts nil without)
(each p params
(if (is p '&o) (do (assert (no opt?) "Duplicate &o:" ',params)
(assert (no key?) "&k cannot precede &o:" ',params)
(= opt? t))
(is p '&k) (do (assert (no key?) "Duplicate &k:" ',params)
(= key? t))
key? (push-end p keys)
opt? (push-end p opts)
(do (assert (~acons p) "Reqd parameters need not be defaulted:" p)
(push-end p reqs))))
(with (n -1 kvs (uniq))
(+ (mappend [list _ `(nth ,(++ n) ,rtargs)] reqs)
(mappend [list (carif _) `(or (nth ,(++ n) ,rtargs)
,(cadrif _))] opts)
(list kvs `(pair (nthcdr ,(++ n) ,rtargs)))
(mappend [list (carif _)
`(or (alref ,kvs ',(carif _))
,(cadrif _))] keys)
)))
,@body))))
also, i find myself not using clos or defstruct all that much in my cl code. i tend to use ordinary data structures like lists and arrays.
my recent example (an excerpt from which i already showed) was a sudoku solver. i wrote one version in CL, then thought i'd do it in arc for fun. in CL, the puzzles were simply arrays (in Arc, they were lists do to the lack of an array type).
However, i didn't want my code to use the array (or list) accessors built into the language to access values in the puzzle, because that is not abstract enough. so i defined a function called pval that takes the puzzle and an x and y, giving the value in that square. that way, my code deals with puzzles, not arrays.
Then I wanted to be able to set the value in a square using the same pval accessor. Trivial in CL, but Arc made it a little difficult. This comes up rather often for me.
That point might not have justified such a long explanation. Whoops.
all i really intend to do is a simple def= macro that will transform (def= pval (val puz x y) (= ((puz y) x) val)) into the previously given defset form. shouldn't be too bad. i have no intentions of mimicing CL's defun. i honestly like arc's def as it is.
The weird thing is that loop seems Arcy-er than iterate cuz it like Arc goes out of its way to minimize parentheses. Worth the learning curve, I assure you.
Didn't Paul call Loop one of the worst mistakes in Common Lisp? I doubt the man is going to change his mind on this one.
Loop may minimize parentheses (i.e. nesting), but then again, so does BASIC. I'd say the main reason it isn't "Arcy" is that it fails the simplicity-of-implementation test.
I think you misunderstand. The "weird" to which I refer is precisely pg's anti-(cl)-loopism. He should love loop not just for the brevity, but also because it is a triumph of DSL. ie, CL loop is a DSL for iteration, and I know because I use it for everything. But! pg seems closer to being a schemer at heart so maybe he prefers recursion for things I handle by iteration.
ps. Are you saying BASIC eliminates parentheses the same way LOOP does? :)
It's hard for me to see Loop as a triumph of DSL. I see it more as a failure of Common Lisp to provide simple flexible iteration constructs.
Arc already seems to provide simple operators for many of the cases in which Loop is commonly used. For example: 'accum', 'for', and 'repeat'. It doesn't seem like we need Loop.
Re BASIC: In a way, yes. Both BASIC and Loop use keywords to replace the indication of structure by parentheses. For example, we could eliminate a pair of brackets in 'with' by doing this:
Well I resisted Loop for almost ten years then broke down and learned it when PCL came out cuz it had a good chapter on it, so having made the transition I can assure you it is a powerful little iteration language, not just a simple iterator of which CL has many so it is not clear what you do not like about those. I'll ignore your continued insistence that BASIC has anything to do with this discussion. :)
Btw, loop offers a little-known second syntax that is Lispy, one just never sees examples in the wild.
If you have found through long experience that Loop truly offers something unique that can't be offered as well (or as intelligibly) by a combination of simpler operators, then I'll have to take your opinion seriously.
I've made a thread to collect exemplary examples of Loop in action (http://arclanguage.org/item?id=2938) and it'd be great if you could contribute your favorites, e.g. from Cells or other code you've written.
Personally, I don't have the same experience as you. I've found Loop to be useful as a replacement for iteration constructs that should have been in CL to begin with, e.g. dovector, when I'm too lazy to code the requisite macro. I haven't ever had to use Loop in its full complexity.
Sorry, it just sounds from what you have posted so far that you do not know CL's loop, by which I mean make an effort to use all of its capabilities such that you would know what's there so well that you had it at your fingertips. I am having the same problem with CL idiots denouncing Arc who probably have not even installed it.
You want me to code (dotimes (x 10) (foo))?! No way!
(loop for y in whatever
for py = (pfft y)
when py collect (yo-mama (cons y py)))
Is the first version starting to look like a disassembly? Bingo!!! :)
btw, the thing that got me to break down and give loop a chance was learning that the expansion was highly optimized code, meaning (for example) it would not first map across whatever and then delete the nils.
but these examples still do not get to the point of LOOP being a DSL. That property emerges only in the next level of application, as even the loop form expands to ten lines. But once you have loop under your belt (I kill myself) you do not even want to code a simple dotimes, that bogus unused count variable just pisses you off no end, never mind all the extra (wait for it) parentheses!
partial application is something I always miss in lisp. I often find myself writing anonymous functions that would be much shorter with partial application. here are a few examples from some of my own CL code (pay attention to the second let binding):
I also enjoy currying and "pointfree" style. However, wouldn't it require giving up functions that take an arbitrary number of arguments? (In fact Scala has both, but it required introducing a special syntax.)
I find that Arc's shortcut closure [] notation is a surprisingly good curry-substitute. For example:
(map (act w) locs-to-check) ; currying
(map [act w _] locs-to-check) ; only one token longer
there's something to that. i didn't really think about it, but that might just be fine. that's actually exactly what scala's explicit partial application looks like, with the underscore.
i honestly haven't been doing a whole lot of practical arc programming. CL and Slime have spoiled me to the point where I find it difficult to use a language that doesn't have that convenience