Arc Forumnew | comments | leaders | submitlogin
New Syntax Feature: Semi-Implicit Local Variables
5 points by drcode 5979 days ago | 15 comments
Hi Everyone- I did a little hacking inside of arc2.tar to implement a cool new syntax feature...

As you probably know, an original feature planned for arc was implicit local variables. This was abandoned because of difficulties with the macro system. The new syntax feature I've implemented is an alternative idea that fills a similar need... Semi-implicit local variables!

The syntax is similar to the bracket syntax for anonymous local functions. Let's say you have the following code:

  (def foo ()
    (let x (+ 1 2)
       (cons x x)))
With my patches you can now write:

  (def foo ()
    {+ 1 2}
    (cons ^ ^))
In order for it to work, you need to be inside of a progn, whether implicit or explicit. (If you don't know what this means, basically you need to be in a "do" or in a function)

When one of the statements in the progn is surrounded by curly brackets, subsequent statements in the progn will have a local variable declared named "^". It will be set to the value of the statement.

Multiple bracketed statements are allowed in a single progn. This also makes it useful for "piping" values along in a chain, allowing you to "flatten" deeply nested code:

  (do {+ 1 5} 
      {* ^ 7} 
      (- ^ 88))
This bracketed anonymous variable notation reduces the cost for creating a single local variable. It is philosophically a cousin of the bracketed function notation. I believe it is easy to understand and in my own personal code I can simplify maybe 2/3 of my functions by using this new feature.

Many functions in arc.arc could be simplified with this feature. To give one example:

BEFORE

  (mac complement (f)
    (let g (uniq)
      `(fn ,g (no (apply ,f ,g)))))
AFTER

  (mac complement (f)
    {uniq}
    `(fn ,^ (no (apply ,f ,^))))
AFAIK, my implementation is 100% working and handles the expected use cases appropriately. It also has no impact on the performance of code that does not use this feature. It is, however, ugly and has poor error handling. That's why I'm not going to mess with anarki in this case, but I don't mind if anyone else wants to make such a change...

To add this feature to your own arc system, replace the function "ac-body" in ac.scm:

  (define (ac-body body env)
    (if (null? body)
        '()
        (if (and (list? (car body)) (eq? 'curly (car (car body))))
            `((let ((_^ (begin ,@(ac-body (cdr (car body)) env))))
               ,@(ac-body (cdr body) env)))
            (cons (ac (car body) env) (ac-body (cdr body) env)))))
Next, you'll need to download a new copy of brackets.arc from here: www.lisperati.com/brackets.scm

Let me know what you think- If anyone else has already implemented something like this I'd love to know about that, as well.



3 points by absz 5979 days ago | link

Hmmm, interesting. The "chaining" aspect reminds me of a pattern that people (e.g. almkglor: http://arclanguage.org/item?id=6955) have found with givens:

  (givens x (+ 1 5)
          y (* x 7)
    (- y 88))
although of course here you have to explicitly name your variables.

-----

5 points by icemaze 5978 days ago | link

Shameless plug: I discussed something similar here http://arclanguage.org/item?id=350

-----

3 points by drcode 5978 days ago | link

I like yours, too...

Yours is far less invasive into the language. The trade-off is it doesn't give you cheap local variable creation, which was my primary motivation.

-----

1 point by icemaze 5978 days ago | link

True. What about ehird's variation (from the same thread)? It's a little more complex but should accomplish what you're looking for, since all the work is done at read time. Didn't try it tho, so give it a spin and let us know ^_^

-----

2 points by drcode 5978 days ago | link

Well, pretty much any solution involving a macro isn't going to do what I want. By necessity, calling a macro requires parentheses and a token. My solution just changes two parentheses to curly braces and requires no extra tokens. It will always be more lightweight than any macro solution. That's the main goal of my feature.

-----

3 points by tokipin 5978 days ago | link

note aand works similarly, with the exception that it shorts on nil:

http://arcfn.com/doc/anaphoric.html#aand

-----

1 point by icemaze 5978 days ago | link

Wow, that's true! I don't use aand much so I didn't notice. Shorting on nil can definitely be a problem because sometimes you want to do something different; it's not very readable since "aand" suggests a boolean result; finally, my macro could be modified in different ways that diverge from the usual aand semantics (like having a special form to break from the pipeline).

Thanks for the comment.

-----

1 point by almkglor 5978 days ago | link

Certainly interesting.

-----

2 points by drcode 5978 days ago | link

ooops... looks like there is a bug when mutation is attempted- I will try to fix this.

This function should return '(foo) but returns nil instead:

  (do {}
      (push 'foo ^)
      ^)
It's a shame this example doesn't work, since it shows off the elegant way you can assign a variable to 'nil by using empty curly braces :-)

-----

2 points by drcode 5978 days ago | link

now fixed. Change ac-body to:

  (define (ac-body body env)
    (if (null? body)
      '()
      (if (and (list? (car body)) (eq? 'curly (car (car body))))
          `((let ((^ (begin ,@(ac-body (cdr (car body)) env))))
             ,@(ac-body (cdr body) (cons '^ env))))
          (cons (ac (car body) env) (ac-body (cdr body) env)))))

-----

1 point by tokipin 5979 days ago | link

this reminds me of what this other clever guy proposed before: http://arclanguage.org/item?id=5586

-----

2 points by almkglor 5978 days ago | link

Interesting. How does it work?

-----

2 points by drcode 5978 days ago | link

in brackets.scm all readings of curly braces {...} get converted into the sexp (curly ...)

Next, when the body of a progn is expanded as part of the arc compilation process, the 'curly token is checked for and replaced with a let.

This means you can also write the following:

  (do (curly (+ 1 2))
      (cons ^ ^))
(I know, I'm polluting the namespace with 'curly... that's would be easy to correct, of course)

-----

1 point by almkglor 5978 days ago | link

Ah, so it's a change in ac.scm ? Hmm. Just out of curiousity, do you think you could do this just in the 'do macro? Although you do lose the ability to do that in the function body. Hmm.

-----

1 point by drcode 5978 days ago | link

I could, but I like the way it works now :-)

Also, since this feature hijacks some valuable characters, it would need to be applicable for far more situations than just 'do, or it would be a waste. I believe it fulfills this requirement.

-----