Arc Forumnew | comments | leaders | submit | absz's commentslogin
3 points by absz 6388 days ago | link | parent | on: Interpreter Manipulation?

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.

-----

3 points by eds 6388 days ago | link

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?

-----

6 points by absz 6388 days ago | link

It sounds like that should work: change the xdef of eval to

  (xdef 'eval (lambda (expr) (arc-prim-eval (ac-denil expr))))
, and then change arc-eval to

  (define (arc-prim-eval expr)
    (eval (ac expr '()) (interaction-environment)))
  
  (define (arc-eval expr) 
    ((eval '__eval) expr))
. The xdef should come soon enough to prevent something from calling a non-existent eval. Then, in Arc, you should be able to do

  (redef eval (expr)
    (prn " - debug - " expr)
    (old expr))
or whatever. Let me test that...

...

...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)

  arc> (mac do+ (a b) `(do (prn ,a) (prn ,b) (+ ,a ,b)))
   - debug - (mac do+ (a b) `(do (prn ,a) (prn ,b) (+ ,a ,b)))
   - debug - (input-history-update
               (mac do+ (a b) `(do (prn ,a) (prn ,b) (+ ,a ,b))))
   - debug - (output-history-update
               (quote #(tagged mac #<procedure>)))
  #3(tagged mac #<procedure>)
  arc> (do+ 1 2)
   - debug - (do+ 1 2)
  1
  2
   - debug - (input-history-update (quote (do+ 1 2)))
   - debug - (output-history-update (quote 3))
  3
. (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?

-----

4 points by absz 6389 days ago | link | parent | on: A question about system calls

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.

-----

3 points by absz 6390 days ago | link | parent | on: String to code

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).

-----

5 points by absz 6390 days ago | link | parent | on: let redundant to with?

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).

-----

3 points by almkglor 6390 days ago | link

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))

-----

2 points by bOR_ 6390 days ago | link

I'm not on anarki, but I've followed this discussion. If you use given rather than let and with, which of the two options below would come out?

arc> (= b "abba") "abba" arc> (let (a b) (list 1 2) (pr a b)) 121 arc> b "abba" arc> (with (a b) (list 1 2) (pr a b)) abbaabba"abba" arc>

-----

2 points by absz 6390 days ago | link

  arc> (given (a b) (list 1 2)
         (pr a b))
  121
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).

-----

4 points by bOR_ 6389 days ago | link

So the main drawback of given is that you need to make your do explicit? mmm, I never liked it implicitly anyway ;).

-----

3 points by absz 6389 days ago | link

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.

-----

3 points by absz 6391 days ago | link | parent | on: The Russian translation of the Arc tutorial.

I don't speak Russian myself, but I want to thank you for the effort—it's important to get stuff published only in English available more broadly.

-----

3 points by absz 6394 days ago | link | parent | on: Don't understand how macros work

Careful! You're evaluating the value of v here, which can break:

  arc> (ontable k v (table 'var 'a)
         (eval `(= ,k ,v)))
  Error: "reference to undefined identifier: __a"
You need to quote the value of v here to prevent it from being evaluated; when you do so, you get

  arc> (ontable k v (table 'var 'a)
         (eval `(= ,k ',v)))
  #hash((var . a))
  arc> var
  a
And then the body of he loop is the same as the body of my assign function from http://arclanguage.org/item?id=6918.

-----

1 point by almkglor 6394 days ago | link

This is correct ^^

-----

6 points by absz 6394 days ago | link | parent | on: Don't understand how macros work

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".

-----

2 points by absz 6395 days ago | link | parent | on: Chalk one up for Arc

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.

  (When you do that,
   it comes out like this.)

-----

2 points by Johnny_65701 6395 days ago | link

I did that and the ends of the lines got all mutilated. Dunno why, I'm copying & pasting from emacs, I'm not doing anything exotic.

-----

2 points by absz 6394 days ago | link

Are you on Windows/editing files created there? If so, is it possible that Windows's "\r\n" line endings are being pasted in literally?

-----

3 points by Johnny_65701 6393 days ago | link

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:

  (defmacro with-hygiene (syms &body body)
   `(let ,(mapcar #'(lambda (s)
                    `(,s (gensym)))
                syms)
    ,@body))
You're supposed to use it like this:

  (defmacro my-stupid-macro (x y z)
    (with-hygeine (a b c)
      `(do-something-with ,x ,y ,z ,a ,b ,c)))
This gets me most of the way there, but what I really want is this:

  (hyg my-stupid-macro (x y z) (a b c)
    `(do-something-with ,x ,y ,z ,a ,b ,c))
We can achieve that like this:

  (defmacro hyg (name args syms &body body)
    `(defmacro ,name ,args
       (with-hygiene ,syms ,@body)))
Now I can do this:

  (hyg swap (a b) (temp) 
    `(progn 
      (setf ,temp ,a) 
      (setf ,a ,b) 
      (setf ,b ,temp)))
Which expands to:

  (DEFMACRO SWAP (A B) 
    (WITH-HYGIENE (TEMP) 
     `(PROGN 
        (SETF ,TEMP ,A) 
        (SETF ,A ,B) 
        (SETF ,B ,TEMP))))
which expands to:

  (PROGN 
    (SETF #:G3089 A) 
     (SETF A B) 
     (SETF B #:G3089))
PS: I'm a Lisp noob, so apologies in advance if something about this is a little naive, or contains a major oversight.

-----

2 points by absz 6393 days ago | link

Are you sure that you're indenting the lines with two extra spaces each (even the first)? That might fix the formatting.

-----

2 points by Johnny_65701 6393 days ago | link

Ooops, you are correct, sir. I indented all the lines except the first. Indenting the first line fixed the formatting (I went back & edited it)

-----

12 points by absz 6395 days ago | link | parent | on: Don't understand how macros work

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.

-----

4 points by zhtw 6394 days ago | link

That explains everything. Thanks!

-----

2 points by absz 6397 days ago | link | parent | on: (delist somelist)

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.

-----

More