Arc Forumnew | comments | leaders | submitlogin
Adding local scope to arc
4 points by ylando 5300 days ago | 7 comments
Here is a code for adding local scope to arc, it can be used for writing in procedural style.

  (= *binding-function-list* '(let with w/table)) 

  (def split@ (lst (o acc)) 
   (if (empty lst) 
       (if (empty acc) 
             nil
             (list (rev acc))) 
       (caris lst '@) (cons (rev acc) 
                            (split@ (cdr lst)))
       (split@ (cdr lst) (cons (car lst) acc))))

  (def span_let (lst)
   (if (empty lst) ()
       (mem (caar lst) *binding-function-list*) 
          (list (append (car lst) (span_let 
                                   (cdr lst))))   
       (and (is (type (caar lst)) 'cons) 
            (is (car (caar lst)) 'scope)) ;scope
          (if (is (cdr (car lst)) nil) 
              (cons (caar lst) (span_let 
                                 (cdr lst)))
              (cons (caar lst) (span_let 
                                 (cons (cdr (car lst)) (cdr lst))))) 
          ;forget @ after scope 
          (if (and (is (cdr (car lst)) nil) 
                   (~is (caar lst) 'fn))          
               (cons (caar lst) (span_let 
                 (cdr lst))) ;not function call
               (cons (car lst) 
                     (span_let (cdr lst))) ) )) 
(mac scope lst (cons 'do (span_let (split@ lst))))

Here is an example of a use of this macro:

  (def shuffle_numbers (min max)
  ;Get a minimum and maximum numbers
  ;Return a shuffle array of numbers from the
  ;minimum number to the maximum
  (scope
     let temp 0 @
     w/table arr @
     for i 0 (- max min) 
        (= arr.i (+ i min)) @ ;init arr
     loop (= i (- max min)) (> i 0) (-- i)
        (swap arr.i (arr (rand (+ 1 i)))) @  
        ;shuffle array elements
     arr ))
I think that the scope macro make the code more readable. What do you think?


5 points by rntz 5300 days ago | link

While the idea of "flattening out" binding forms appeals to me, since complex arc and lisp function bodies in general tend to become very nested, I don't really like the way this macro conflates flattening binding forms with automatic parenthisisation of sequential "statements". I don't see why I shouldn't just write this:

    (def shuffle-numbers (min max)
      (scope
        w/table arr @
        (for i 0 (- max min)
          (= arr.i (+i min)))
        (down i (- max min 1)
          (swap arr.i (arr (rand (+ 1 i)))))))
At the very least, I don't like the way @ "magically" differentiates between binding forms and sequential forms based on the contents of binding-function-list* . It would be better IMO to have two different special symbols - one for binding forms, one for sequential evaluation.

Although, as far as magic goes, there's a neat trick that you could do using the 'sig table, which stores signatures (that is, the parameter lists) of functions and macros. You could have 'scope or some similar macro (let's say 'magic-scope) use 'sig to look up the parameter lists of the binding macros it encounters, and use them to determine "where to put the @s". So you'd just write, for example:

    (magic-scope
      w/uniq (foo bar)
      let xyzzy (+ plugh quux)
      `(,xyzzy (,foo ,bar)))
With your macro as is, you'd have to add 'w/uniq to binding-function-list* and also insert @s, like so:

    (scope
      w/uniq (foo bar) @
      let xyzzy (+ plugh quux) @
      `(,xyzzy (,foo ,bar)))

-----

4 points by rntz 5299 days ago | link

I've been toying with clojure recently, and there are two macros, -> and ->>, which essentially "thread" expressions through a series of forms, like so:

  (-> x (f a) (g b))
  ; macroexpands to
  (g (f x a) b)
  
  (->> x (f a) (g b))
  ; macroexpands to
  (g b (f a x))
It occurs to me that (magic-)scope is very similar to a reversed-argument form of ->>; let's call it '<<-:

  (<<-
    (w/uniq (foo bar))
    (let xyzzy (+ plugh quux))
    `(,xyzzy (,foo ,bar)))
which is equivalent to (but for this use case, more readable than):

  (->> `(xyzzy (,foo ,bar)) (let xyzzy (+ plugh quux)) (w/uniq (foo bar)))
both of which macroexpand to:

  (w/uniq (foo bar)
    (let xyzzy (+ plugh quux)
      `(,xyzzy (,foo ,bar))))
This <<- form has the advantage of not depending on any sig/binding-function-list* magic, at the expense of a few sets of parentheses. And, as I mentioned, it has a nice symmetry with ->/->>, both of which are useful macros in their own right.

-----

4 points by rocketnia 5300 days ago | link

First off, two bugs: You use 'append rather than 'join, and 'shuffle_numbers will overwrite the global variable 'i since 'i isn't a lexical variable at (= i (- max min)) and (-- i). There might a third bug where you say (~is (caar lst) 'fn), but I'm not sure what your intention is there.

Next, there are some things you can simplify. In shuffle_members, you have "let temp 0 @", which isn't doing much, and you also have "@ arr" at the end, which is unnecessary. Unlike 'let, 'w/table automatically returns the value its variable has at the end of the body. In span_let, there's a place where you check [and (is (type _) 'cons) (is (car _) 'scope)], but FYI, that's exactly what [iscar _ 'scope] does. :) Also, I don't expect you to know this right away, but you can accomplish the decreasing loop using (down i (- max min) 1 ...); 'down is a backwards 'for.

Finally, it's really not a big deal, but these names would be more consistent with Arc:

  binding-function-list*
  span-let
  shuffle-numbers
I think that the scope macro make the code more readable. What do you think?

I don't really think it makes it more readable, but that's only because it introduces new rules I'm not accustomed to. The "let temp 0" line is clearly supposed to wrap the whole area, but I had to open up Arc and check to see whether the 'for loop wrapped the 'loop loop or not. (Your indentation might have been a clue, but I suspected a bug.)

In total, you removed ((()())) and put in (scope @ @ @ @) and some extra rules to worry about. I think that's sort of a net loss. But hey, it could be the basis for a better idea. If it helps you read your code, then I think it's worth it already. ^_^

-----

1 point by fallintothis 5300 days ago | link

that's exactly what [iscar _ 'scope] does.

In vanilla Arc, [caris _ 'scope].

-----

1 point by rocketnia 5299 days ago | link

Typo. XP Thanks.

-----

1 point by ylando 5300 days ago | link

Thanks for your nice responce. It was very helpfull. Append works in jarc but i didn't find any document on this function.

-----

4 points by ylando 5300 days ago | link

Thanks to the remark of rocketnia. I have found some bugs in the code. Here is the code after I remove the bugs:

   (def span-let (lst)
   (if (empty lst) ()
       (mem (caar lst) binding-function-list*) 
          (list (join (car lst) (span-let (cdr lst))))   
       (and (is (type (caar lst)) 'cons) (caris (caar lst) 'scope)) ;scope
          (if (empty (cdr (car lst))) 
              (cons (caar lst) (span-let (cdr lst)))
              (cons (caar lst) (span-let (cons (cdr (car lst)) (cdr lst))))) ;forget @ after scope 
      (cons (car lst) (span-let (cdr lst)))))

  (def shuffle-numbers (min max)
  ;Get a minimum and maximum numbers
  ;Return a shuffle array of numbers from the minimum number to the maximum
  (scope
     w/table arr @
     for i 0 (- max min) 
        (= arr.i (+ i min)) @ ; init arr
     down i (- max min) 1
        (swap arr.i (arr (rand (+ 1 i)))) @  ; shuffle array elements
  ))  
Scope get a list of function calls and macro calls; so I add a macro for returning a value:

  (mac id (x) `,x)

-----