Not quite—from Bogart's post, it sounds like they want to be able to write (= eval my-magic-fn), and then (+ a b) would be passed through the new eval too. This would be interesting; I've been thinking about similar things.
Ok, so you can't just redefine 'eval, you need to redefine 'eval and have MzScheme use that function on the Scheme side of things as well.
It looks like the 'arc-eval function is basically the one you want to redefine, since that is the one that is called inside the definition of 'tl2. In fact, the definition of 'eval is exactly the same as 'arc-eval except 'eval calls 'ac-denil on its arguments before compiling them. So basically what you would want to do would be to look up the value of the arc-side eval function and call that instead of hard coding a call to 'arc-eval. Right?
...yes, this works! Well, almost. I didn't really test it, but (a) ordinary things seem to get evaluated fine, and (b) the message is printed. It doesn't seem to hit everything in a macro-expansion, but I can't tell whether or not it should. For instance, I get (output reformatted for clarity)
. (Of course, its output didn't have extra line breaks and had quasiquote instead of `, etc.) I feel like it should print
- debug - (do (prn 1) (prn 2) (+ 1 2))
too.
<EDIT>
The problem is that ac-mac-call calls ac directly so that it can pass the current environment as a second argument. Perhaps arc-eval should take an optional environment argument too? But that would clutter things unnecessarily on the Arc side... anyway, the point is, after looking at it for all of five minutes, macros are tricky. (And I haven't even looked at ac-macex yet...) For instance, a simple test:
arc> (nil 1 2)
Error: "string-ref: index 0 out of range for empty string"
arc> (redef eval (expr)
(if (and (cons? expr) (~car expr))
(cdr expr)
(old expr)))
#<procedure: eval>
arc> (nil 1 2)
(1 2)
arc> (mac donil (a b) `(nil ,a ,b))
#3(tagged mac #<procedure>)
arc> (donil 1 2) ; Should return (1 2), but:
Error: "string-ref: index 0 out of range for empty string"
</EDIT>
The downside is that, if it isn't slow now, it might well prohibit certain optimizations (though reading Steve Yegge's recent talk makes me think that I might well be wrong). Is this worth pushing on Anarki?
Try running (system "echo $PATH"); you probably don't have 'convert', in your path via system. If you specify the exact path (e.g.(system:string "/usr/local/bin/convert" old " -flip " new)), then it should work. Also, be careful about passing old or new in to system unmodified—quote and escape them, or you might run anything at all.
It's probably a better idea to have your monkeys copy code trees around; i.e., rather than having (= monkey1!code "(+ x y)"), have (= monkey1!code (read "(+ x y)")), which is the same as (= monkey1!code '(+ x y)). A scheme like this (if you are careful about what you do with symbols) is a better idea because it's more Lisplike and you can thus take advantage of Lisp's strengths (list manipulation and code-is-data).
I've been programming with given(s) for a little while now, and I really like it. Can't say why I'm so vehement, but it's definitely a Good Thing™. Thank you for bringing this up again (and a "thank you" to aidenn0, if he/she is still reading these fora, for suggesting this in the first place).
An interesting bit about 'givens is that it makes functional programming in an imperative style almost seamless:
(givens f (car n)
v f!v
_ (prn v) ; v isn't being set properly in some cases for some reason...
l (combinatorics f v)
_ (prn l) ; debugprint
(is l 'undef))
The difference between given and let/with is that you cannot have more than one statement in the body of the given. For instance:
arc> (given (a b) (list 1 2)
(prn "Here")
(pr a b))
Error: "Can't understand fn arg list \"Here\""
What's happening here is that given is trying to bind (prn "Here") to (pr a b), but (naturally) can't bind a string; it takes the first n args, where n is the largest even number less than or equal to the number of arguments provided, and interprets them as variables. You must, therefore, write
arc> (given (a b) (list 1 2)
(do
(prn "Here")
(pr a b))
Here
121
.
Also, I highly recommend switching to Anarki. It's got bugfixes and enhancements galore (and even runs on the newest version of mzscheme).
It's the only "drawback", if such it is. I feel that since you need extra parentheses for either the body or the variables, it's better to wrap the body in a do, since that's less common.
That's not quite true. You would not see Lisp code written as though it were assembly, full of gotos and working with a finite number of registers, to take an extreme example. Lisp is a multiparadigm language, but it is heavily functional. As such, writing imperative code is not generally advisable ("not in the spirit of Lisp"), but is possible. Similarly, code with such a heavy use of variables is likely unfunctional, and thus would similarly be outside the "spirit of Lisp". Yes, you can do anything you want in Lisp (cf. the Church-Turing thesis), but it may not be advisable ("in the spirit of Lisp"). Again, for instance, if you want to work in a stack-based manner, it would probably be advisable to either (1) rework your code, or (2) switch to a stack-based language like Factor.
This is not unique to Lisp: if you want to write a tail-recursive, functional, list-based program in C, that's probably not a good idea. You can, but since C is optimized for iteration (with many implementations not even supporting tail call elimination) and for imperative programming (it lacks closures and anonymous functions), and since memory management in C is…clunky…you would be better off (1) reworking your code, or (2) switching to a more functional language with lists and tail call elimination, such as a Lisp.
In short, yes, as a Turing-complete language, Lisp can do anything. And as a multi-paradigm language (with macros), it can do a good job at performing a given task in any way. But it has strengths, inclinations, and intentions, which together do comprise what could be called a "spirit of Lisp".
To post code that looks like code, surround it by blank lines and indent each line with two spaces—that should allow the code to display in a sane manner.
Nope, Linux. But let me try again, since I have a different macro now that's considerably simpler, so the loss of newlines isn't as bad.
The original one I wrote wasn't working quite right. Then I found something in On Lisp, a macro called with-gensyms that I renamed with-hygeine due to a symbol conflict with something in Clisp:
As cchooper observed, macros are expanded at compile time, not runtime. This means that they cannot see lexical variables like k. (Since x was a global variable, it could be seen at compile time, thus making your first example work.) In order to look at the lexical environment, you need an ordinary function:
(def assign (name expr)
(eval `(= ,name ',expr)))
The reason you don't need a macro here is that you aren't actually suppressing evaluation, which is what macros do; you want the values of name and expr. Thus, assign should be a function, which runs at runtime. Also note that expr needs to be quoted, or you attempt to evaluate it, which fails if it is a symbol.
The one thing that might trip you up with this function is that it (like =) will not create lexical variables, but only globals, unless lexicals of the names you are about to assign are already declared.
Also, a formatting tip: to get text to appear as code, surround it by blank lines and indent it by two spaces.
The apply function takes a function, any number of arguments, and a list, to wit (apply f x0 x1 x2 ... xN args). This is the same as (f x0 x1 x2 ... xN args.0 args.1 args.2 ... args.M). This allows you to construct argument lists at runtime, which can come in very handy.