Arc Forumnew | comments | leaders | submit | vincenz's commentslogin
22 points by vincenz 6342 days ago | link | parent | on: Can Arc run as a script instead of REPL?

Yes, quite easily, just make a new script like this:

ar.sh:

    #! /bin/sh
    #|
    exec mzscheme -fmv "$0" ${1+"$@"}
    |#
    (require mzscheme)
    (load "ac.scm") 
    (require "brackets.scm")
    (use-bracket-readtable)
    (aload "arc.arc")
    (aload "libs.arc")
    (aload (car (vector->list (current-command-line-arguments))))
main.arc:

    (whilet line (readline)
      (prn line))
shell:

    chmod +x ar.sh
    ar.sh main.arc

-----

2 points by kens 6341 days ago | link

Thank you! That's hugely helpful! I have to say, though, that 11 lines of boilerplate kind of obliterates Arc's conciseness advantage in the Arc Challenge :-)

-----

2 points by jfm3 6341 days ago | link

Not really, the Arc Challenge specifically discounts code for setting up libraries. All the script is doing is setting up libraries and loading your code.

-----

1 point by shader 5965 days ago | link

Is there a way to execute a script that a) runs some arc or scheme code (specifically "(thread (asv))" or similar) and then b) starts the repl? Do I just change your main.arc to run the arc code I want, and then call ($ (tl))?

-----

1 point by laughingboy 6339 days ago | link

This works great, but what exactly does `"$0" ${1+"$@"}` mean? I'm not a keen shell scripter.

-----

4 points by bayareaguy 6339 days ago | link

This is the common sh shell idiom to properly pass the original command line to the program being invoked.

"$0" expands to the program name ("ar.sh" in his example).

${1+"$@"} is conditional:

if $1 (the first positional parameter) is unset it expands to nothing.

however if $1 is set, it expands to "$@", which in turn expands to all the parameters, each one quoted as a separate word.

-----

1 point by laughingboy 6339 days ago | link

Thank you.

-----

1 point by byronsalty 6324 days ago | link

I'm wondering about this #| .. |# syntax. Never seen that before.

-----

2 points by cadaver 6324 days ago | link

It's the scheme way to write multiline comments (possibly other lisps?). It's specified in the R6RS.

-----

1 point by kennytilton 6324 days ago | link

Also Common Lisp, and this reminds me that this is something I missed when working on some Arc code.

-----

2 points by cadaver 6324 days ago | link

#| ... |# works in Arc too. Though whether by design or because mzscheme supports it, I don't know.

-----

1 point by kennytilton 6324 days ago | link

Doh! :)

-----


Out of curiousity,

How do you run this?

-----

6 points by willpost 6342 days ago | link

Not really Arc related, but here you go in 3 steps:

1 - Paste the following code into a new document test.ss and save in servlet example folder (/usr/plt/collects/web-server/default-web-root/servlets/examples/test.ss) If document name test.ss changes, change the module name on first line.

(module test mzscheme (require (lib "servlet.ss" "web-server")) (provide (all-defined)) (define interface-version 'v1) (define timeout +inf.0)

  (define cc (make-parameter #f))

  (define (input name) `(input ((type "text") (name ,(format "~a" name)))))
  (define (submit) `(input ((type "submit"))))
  (define (form . body) `(form ((action ,(cc))) ,@body))
  (define (a ref . body) `(a ((href ,ref)) ,@body))
  (define-syntax page
    (syntax-rules ()
      [(page x ...) (send/suspend (lambda (k) (cc k) `(html (body ,x ...))))]))
  (define (get name req)
    (extract-binding/single name (request-bindings req)))

  (define (start initial-request)
    (define foo (get 'foo (page (form (input 'foo) (submit)))))
    (page (a (cc) "click here"))
    (page "you said: " foo))
  
)

2 - Start the PLT web server sudo /usr/plt/bin/plt-web-server

3 - Open a web browser and navigate to http://localhost/servlets/examples/test.ss

An easy way to check for errors is to open it in DrScheme, click "Run" and they would be highlighted in red.

-----

1 point by vincenz 6342 days ago | link | parent | on: The real problem with unhygienic macros

This was brought up before, namely here:

http://arclanguage.com/item?id=804

And there was a partial solution as well.

-----


Finally, a big clean for 'ac-call'

    (define (ac-call fn args env)
      (let ((macfn (ac-macro? fn)))
        (if macfn
          (ac-mac-call macfn args env)
          (let ((afn (ac fn env))
                (aargs (map (lambda (x) (ac x env)) args))
                (nargs (length args)))
            (cond 
              ((eqv? (xcar fn) 'fn)
               `(,afn ,@aargs))
              ((and (>= nargs 0) (<= nargs 4))
               `(,(string->symbol (string-append "ar-funcall" (number->string nargs)))
                                  ,afn ,@aargs))
               (#t
                `(ar-apply ,afn (list ,@aargs))))))))

-----


Idem for 'ac-macex' and cleaning up some more

    (define (ac-macex e . once)
      (let ((m (ac-macro? (xcar e))))
        (if m
          (let ((expansion (ac-denil (apply m (map ac-niltree (cdr e))))))
            (if (null? once) (ac-macex expansion) expansion))
          e))
      )

-----


Idem for 'ac-complex-args?'

    (define (ac-complex-args? args)
      (cond ((eqv? args '()) #f)
            ((symbol? args) #f)
            ((symbol? (xcar args))
             (ac-complex-args? (cdr args)))
            (#t #t)))

-----


Idem for 'ac-qq1'

    (define (ac-qq1 level x env)
      (cond ((= level 0)
             (ac x env))
            ((eqv? (xcar x) 'unquote)
             (list 'unquote (ac-qq1 (- level 1) (cadr x) env)))
            ((and (eqv? (xcar x) 'unquote-splicing) (= level 1))
             (list 'unquote-splicing
                   (list 'ar-nil-terminate (ac-qq1 (- level 1) (cadr x) env))))
            ((eqv? (xcar x) 'quasiquote)
             (list 'quasiquote (ac-qq1 (+ level 1) (cadr x) env)))
            ((pair? x)
             (map (lambda (x) (ac-qq1 level x env)) x))
            (#t x)))

-----


Seems that to get first-class macros one would have to:

    1) Not evaluate arguments directly in ac-call, but keep the original arguments and envs
    2) Add a clause in ar-apply to deal with macros
    3) Make sure that in ar-apply, arguments are evaluated for the case where it is still a function/list/hashtable and not a macro
Basically use lazyness only until we find out we're calling a macro.

I'd implement it, but I'm not certain pg wants to go this direction. I don't think it'd change anything else to the language even though under-the-hood it uses some lazy techniques.

-----


As a last food for thought: Is it really a good idea to 'eval' code when 'compiling' it? Shouldn't there be some clean way to stage it then?

-----


I finally found the bug, the problem is that no matter how you pitch it, you have to go through 'eval' to get an actual tagged value, and since eval is done per top-form, you'll never get the first form (annotate 'mac id) to evaluate and form a macro-value to use on 1. Shame :|

While exploring this, I modified the code some more, notice that w.r.t. to the other suggestion I made to ac-call (which was completely backwards compatible), the ac-call here changes only by -1- letter.

    (define (ac-call fn args env)
      (let ((afn (ac fn env))
            (macfn (ac-macro? afn)))
        (cond (macfn
               (ac-mac-call macfn args env))
              ((and (pair? fn) (eqv? (car fn) 'fn))
               `(,afn ,@(map (lambda (x) (ac x env)) args)))
              ((= (length args) 0)
               `(ar-funcall0 ,afn ,@(map (lambda (x) (ac x env)) args)))
              ((= (length args) 1)
               `(ar-funcall1 ,afn ,@(map (lambda (x) (ac x env)) args)))
              ((= (length args) 2)
               `(ar-funcall2 , afn ,@(map (lambda (x) (ac x env)) args)))
              ((= (length args) 3)
               `(ar-funcall3 ,afn ,@(map (lambda (x) (ac x env)) args)))
              ((= (length args) 4)
               `(ar-funcall4 ,afn ,@(map (lambda (x) (ac x env)) args)))
              (#t
               `(ar-apply ,afn
                          (list ,@(map (lambda (x) (ac x env)) args)))))))
    
    ; returns #f or the macro function
    (define (ac-macro? fn)
      (let ((v
              (if (symbol? fn)
                (namespace-variable-value fn
                                          #t
                                          (lambda () #f))
                fn)))
        (if (and v
                 (ar-tagged? v)
                 (eq? (ar-type v) 'mac))
          (ar-rep v)
          #f)))

-----

More