Arc Forumnew | comments | leaders | submit | elibarzilay's commentslogin
3 points by elibarzilay 5846 days ago | link | parent | on: Request for Bugs

First of all, updating to the recent plt version will be good, since there have been a lot of optimizations since then, including a lot of work to the compiler and the jitter.

Arc needs to do some list marshaling in any case to deal with nil -- and as such it reimplements nearly all of the list operations, deferring to mzscheme functions only for a few things. Because of this, using `mcons' shouldn't be too problematic (not even for speed, since arc functions in general are not careful about cyclic lists, a plain implementation will be fast enough -- for example, mzscheme's `map' is now implemented in scheme). I have done some tests in the past to measure

Probably the only major problem is how to deal with a rest argument. One option is to make it illegal to mutate rest arguments. Another is to hook on the fact that rest arguments are not modified often -- so instead of copying them to a mutable list on every call, they can be converted only when needed, by the functions that change the contents of these lists. Given the above (need to implement list functionality anyway), this wouldn't be difficult -- basically some wrapper around a mzscheme list that can do the necessary work. As a side benefit it can be used to deal with nil too, which will make that run faster as well.

(BTW, this will be much faster than using r6rs or r5rs modes.)

-----

1 point by CatDancer 5845 days ago | link

One option is to make it illegal to mutate rest arguments.

Currently, in arc.arc, list is defined as

  (def list args args)
so this would need to change if rest arguments couldn't be mutated. (Which would be OK with me, I can't think of a single case where I ever mutated a rest argument).

Another is to hook on the fact that rest arguments are not modified often -- ... they can be converted only when needed

How could this possibly work??

  ((fn args
     (= (cadr args) 'X)
     args)
   'a 'b 'c)
sure, it would be easy for set-cdr! to see that the second pair is immutable and decide to create a new mutable cons. But args is still going to be pointing to the original list of immutable pairs!

As a side benefit it can be used to deal with nil too, which will make that run faster as well.

Can you explain? (I need smaller steps to be able to follow you :-)

(BTW, this will be much faster than using r6rs or r5rs modes.)

Yes, I was getting that impression browsing through the r6rs and r5rs code.

The way I'm leaning right now is to first rewrite the Arc compiler to generate PLT 4 code in the simplest possible way, for example to always convert rest arguments to mutable lists. Then, things like making rest arguments immutable could be done as an optimization if desired.

-----

1 point by elibarzilay 5845 days ago | link

For the "on-demand" conversion some PLT magic will be needed -- I'm basically talking about doing something at the level of the PLT function that is the result of compiling an arc function. In any case, explaining more through this medium will be hard for me...

-----

1 point by CatDancer 5845 days ago | link

Send me an email at cat@catdancer.ws, or use a pastebin and post the link here.

-----

2 points by elibarzilay 6240 days ago | link | parent | on: fork() in Arc

The underlying foreign implementation is looking for the library. The problem is that /usr/lib/libc.so is not the library and dlopen() does not know how to handle this.

A better alternative with mzscheme's foreign interface is to use `#f' for the library -- that treats the mzscheme binary as the library to open (which includes the usual libc stuff.)

-----

1 point by sacado 6240 days ago | link

Interesting. Should be added to ffi.arc...

-----

2 points by eds 6240 days ago | link

Yeah, because currently if you attempt

  (w/ffi #f
    (cdef cfork "fork" cint ()))
arc will convert #f to nil, which mzcheme thinks is an unbound variable. There are work-arounds, like

  (w/ffi (read "#f")
    (cdef cfork "fork" cint ()))
but this is rather ugly.

-----

4 points by elibarzilay 6291 days ago | link | parent | on: Arc omptimization, again

Repeating any single string several times looks bad (and might be bad for other reasons), but should not lead to a performance hit. Consider the fact that each of these occurrences that you see is just a pointer to the single string that was created when the code was read in.

-----

1 point by sacado 6291 days ago | link

I was wondering, about the delay during the initial interpretation of (load "arc.arc") and (load "libs.arc") everytime arc is invoked : is that possible to load (and eval) these files once in mzscheme and then save the whole memory in an executable, à la SBCL (maybe other CLs, too) ? I couldn't find it in mzc's doc or in mzscheme's functions, at least in the older versions. That will not solve the problems after loading, I guess, but this one is quite boring (I find).

-----

1 point by elibarzilay 6289 days ago | link

It is possible to byte-compile it, which will get rid of the delay. It is also possible to create an executable with the compiled files in.

-----

3 points by elibarzilay 6298 days ago | link | parent | on: New version

That was fixed in version 371.

-----

1 point by elibarzilay 6298 days ago | link | parent | on: Eval Inquirry

He also wanted to be able to have hash tables in eval, which is easy to get by adding another line to the above, allowing `hash-table?' as literals.

-----


"interpreter interpreted from another interpreter" is very far from the way things are. Arc works by translating arc forms to mzscheme, so it's a compiler, not an interpreter. The arc compiler itself is not interpreted, it is compiled by mzscheme -- and since mzscheme byte-compiles everything (and uses a JIT compiler on the result), it is not an interpreter.

The thing is that there are many ways in which arc can be made faster, but packaging things up in a mzscheme executable is not what speeds things up.

-----

5 points by nex3 6298 days ago | link

Except that according to sacado's benchmarks, packaging things up in a mzscheme executable apparently does speed things up.

-----

2 points by eds 6298 days ago | link

Um....

Sacado said: "Well, actually, to do so, I had to make effective the optimisation someone there already wrote about, in arc< and arc>."

-----

5 points by sacado 6298 days ago | link

In fact, both were needed to optimize things. Mzscheme's doc says explicitly (though I can't remeber where exactly) that standalones are a little faster than interpreted code (there are a few more optimisations they can perform during compilation I guess). Running arc1.scm through mzscheme -f is a little slower.

-----

4 points by elibarzilay 6297 days ago | link

There is an initial overhead for byte-compiling the code and jitting it -- but that's not something that you'd be able to measure for such a small piece of code. Once that's done, it's the same code -- mzscheme (since a good while ago) on intel and ppc does not interpret code. Ever. Even on solaris, where the jit is disabled, it's "interpreting" byte-compiled code, so it is not an interpreter in any case.

-----

2 points by elibarzilay 6298 days ago | link

Right, these two are major bottlenecks. (There are still a good number of them that can be optimized.)

-----


A real solution for this will be really hard. For example, I'd expect this work:

  (mac foo () 1)
  (def bar () (let x (foo) (fn () x)))
  (= x (list (bar)))
  (mac foo () 2)
  ((car x))
    --> 2
(And the usual solution is to lower your expectations...)

-----

1 point by shiro 6310 days ago | link

Why? No matter whether foo is a macro or a function, a closed value of 'x' is fixed in the closure returnd by bar, so redefining foo shouldn't affect the closure bound to x. (But the next call of bar returns a closure that closes 2 as 'x'). Am I missing something?

-----

2 points by elibarzilay 6309 days ago | link

Like I said -- the usual solution, which you think about, is to lower your expectations. And I'm not joking when I say that: you really just decide to expect that `x' is not going to changed since it's "closed", so you're happy with the result. Some people set their expectations lower, and they expect a macro redefinition to not change anything about existing bindings, so they won't think about your hack as necessary.

And BTW, this is not just some cooked up academic non-problem. It's a practical issue. Consider this example:

  (mac foo () ...)
  (def bar ()
    ...do something...
    ...change foo...
    ...continue doing stuff...)
If that macro change will (with your suggestion) lead to recompiling `bar', but that will not affect the current active call. This is a very common problem with hacking live servers (as pg often talk about):

* You cannot change the main server handler loop unless you make it call itself via its name, so the recursive call will use the new definition.

* Even if you do, you should still be extremely careful since you might change the code when there's a live thread around which was compiled with the old macros, and might rely on the old macros. For example, `f1' and `f2' use `foo', `f1' is invoked in a thread, now `foo' gets redefined, after that's done `f1' continues to call `f2': old code is calling new one, things break.

-----

1 point by shiro 6309 days ago | link

I still believe you are talking different things. My "expectation" is derived from these axioms:

* Macro is a local source-code transformation that replaces macro call with the macro definition (with some hygienic magic, if you prefer).

* The language adopts applicative order.

If you want redefining foo to affect the value of the closed variable 'x', then either you have a non-trivial definition of macros (macro transformation requires transformation of code surrounding the macro call) or you are adopting different evaluation semantics (like x is bound by call-by-name).

Note that your problem occurs even foo is a procedure. It's not a macro problem. It's a problem of semantics of closed variables.

-----

1 point by elibarzilay 6313 days ago | link | parent | on: Using Common Lisp as base for Arc

MzScheme does have a JIT compiler that produces machine code. One of the reasons to use the recent version (372) is that in version 370 it switched to a precise GC which is faster and more stable. (BTW, Chicken compiles to machine code when used in batch mode.)

-----


With the PLT web server:

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

-----

38 points by GreyLensman 6315 days ago | link

What is worth noting in the above post is the "With the PLT web server" part.

What I mean is most of the other posts demo how to do it with some required (exotic) framework. This example is just using the basic web server APIs of the PLT continuation based web server.

In other words in this example, Eli BOTH writes the framework (in the first 9 lines), then uses the framework in the last 4 lines.

PLT's mzscheme is in effect, a very distant cousin to Scheme. If they weren't all eggheads and had at least one attentive marketing person, they would have labeled it some new fangled SuperLispIncarnateNextGeneration (SLING) moniker and received a bit of buzz.

I haven't looked at exactly how PG implemented Arc over MzScheme, but consider this. Much of what he did _could_ be done using no more then PLT mzscheme's out of the box, macro system, custom language module capability and custom reader capability, by anyone. Yes, even you.

In some sense most of Arc is just what anyone does who develops in PLTs Scheme/laguage system, i.e., create their own custom language/DSL. PG's may have done it a bit better, more extensively, with a general purpose intent.

So what is the secret weapon here Arc, or the underlying system that allows someone to create an Arc so easily?

You could, right now, be using PLT MzScheme to do your own Arc(s), your own, just for you and your friends custom language/DSL.

Ruby, Python are cute, and deserve having a user base, but PLT mzscheme is without doubt, the best and most powerful language system out there that "no one talks about."

-----

19 points by matthiasf 6314 days ago | link

Thanks for the highly accurate description of PLT and PLT Scheme. I founded PLT as an academic but I am one who appreciates the necessity to open a channel of communication between the 'real' world and academia.

While Matthew and Robby are the "drivers" (with lots of co-pilots :-) I think that setting this tone early in the project has placed PLT Scheme naturally in a lonely niche: it is a real scripting language with capabilities that rival those of everything out there and it is also a serious academic infrastructure. Typed Scheme -- the first and only sound 'gradual typing' language so far -- is just an example of what I mean. We can publish about this in the flagship research conference on programming languages and at the same time, we are using it for its intended applications. Sam Tobin-Hochstadt is porting a part of DrScheme to Typed Scheme as I type. It is this kind of experiment -- porting a piece that your "life" depends on -- that puts us squarely on the applied side, too.

-- Matthias

-----

2 points by NickSmith 6313 days ago | link

Thanks Grey. I am in the process of teaching myself Scheme (via SIPS) and find your comment quite enlightening.

-----

2 points by croach 6312 days ago | link

Hi Nick,

I'm currently in the process of teaching myself Scheme as well through a combination of HtDP and SICP and I was wondering what SIPS is? Perhaps it's another source I should check into.

-----

1 point by NickSmith 6312 days ago | link

I perhaps need to first teach myself to type. I meant SICP.

-----

1 point by croach 6311 days ago | link

Heh, that's funny and disappointing all at the same time--I was really hoping there was another really great resource on the web for learning Scheme. Thanks for the reply.

-----

4 points by NickSmith 6310 days ago | link

Sorry to disappoint, but here's a book that I do recommend: http://www.amazon.com/Little-Schemer-Daniel-P-Friedman/dp/02...

It's quite short and set out in a question and answers style. The thing is, it very gently and very subtlety bends your mind around to the Lisp way of thinking. After reading this, SICP seems so much more accessible.

There are also two follow on books - The Reasoned Schemer and The Seasoned Schemer which are again quite short and use the same interactive approach. I haven't had chance to read them yet but they get rave reviews on Amazon

-----

2 points by vincenz 6312 days ago | link

Out of curiousity,

How do you run this?

-----

6 points by willpost 6312 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.

-----

2 points by elibarzilay 6316 days ago | link | parent | on: (un)hygienic macros

Off topic, but:

* The define-syntax...syntax-rules boilerplate is a very-easy-to-bypass thing.

* Small applications are not a good justification for unhygienic macros: they bite much more when there are several people involved, or when a project lives for a long time.

* Adding automatic gensyms is not enough to call the result hygienic.

-----

1 point by icemaze 6316 days ago | link

1. I didn't know that, I never managed to get past that point. :P Are there macros like with-gensyms that make your life easier? Could you give a reference to show more precisely what you mean? That would be really nice.

2. What I mean is that variable capture is even more useful in big projects (because the DSL approach tends to be more powerful). It is true that they could cause nastier bugs if used incorrectly. But if you use them correctly (by forcing an order of evaluation and protecting the variables you use) there should be no problems. That's certainly true for a LISP-2, I'm not sure about Arc.

3. I don't care how you call it: if you use gensyms where needed the resulting macros won't screw you up! That's what matters, doesn't it?

-----

3 points by elibarzilay 6316 days ago | link

1. You complained about scheme having too much boilerplate code. This is easy to fix, for example, with this macro-defining-macros:

  (define-syntax defmac
    (syntax-rules ()
      ((defmac (name x ...) body)
       (define-syntax name
         (syntax-rules () ((name x ...) body))))))
you can write:

  (defmac (unless condition body ...)
    (when (not condition) body ...))
(See also item 4.)

2. Yes, DSLs are even more useful in big projects; obviously, the only source of all problems is using something incorrectly, the problem is how easy it is to write something incorrect; evaluation order has nothing to do with macros; protecting variables you use doesn't cover all problems; lisp-2 is "statistically better" because people shadow function names less frequently (see also next item).

3. They will bite -- even if you always use gensyms. A simple example:

  (def twice (func) [func (func _)])
  (mac with-duplicate (func . body)
    `(let ,func (twice ,func) ,@body))
This looks simple enough -- but `with-duplicates' happily inserts a `twice' symbol, regardless of what it happens to be bound to:

  (let twice [+ _ _] (with-duplicate cdr (cdr '(1 2 3 4))))
4. Extra point: doing simple captures in scheme with `define-syntax' is said to be difficult in the general case, but it is possible to make that easy too. For example, see the macro definition and examples in http://tmp.barzilay.org/defmac.ss -- the definition uses mzscheme's `syntax-case' etc, but this is not something that you should know about to use it. Just skip to the example to see how it works. (Cheat: there are subtle cases that this doesn't work.)

-----

1 point by icemaze 6315 days ago | link

1. Yes, that's much nicer, thanks.

2. "the problem is how easy it is to write something incorrect". Very true.

"evaluation order has nothing to do with macros" I'm sorry but that's incorrect. An example will show you why:

  (mac sum (x y) `(+ ,y ,x)) ; This is a toy, but some macros suffer from the same problem without being so trivial/stupid
  (= x 1)
  (+ x (++ x))   ; -> 3, the expected result
  (= x 1)
  (sum x (++ x)) ; -> 4, wrong result
Common Lisp macros usually solve this with a meta-macro called once-only that forces things to be evaluated in the right order and only once. Google it for more info.

"lisp-2 is statistically better". Well, not if people know that shadowing function can cause problems. Now a question arises: does shadowing functions solve problems in a way not possible with other methods? Does shadowing function make programs shorter? Or, equivalently, is shadowing functions really that useful? (Please provide examples if you answer this). If not, using a lisp-2 and being careful is enough. But yes, it's easy to overlook a symbol and make a mistake that can be costly. On that, I agree.

3. You are right and that's why I said "I'm not sure about [a lisp-1 like] Arc".

4. That seems really interesting, I'll look into it.

-----

1 point by elibarzilay 6315 days ago | link

You didn't understand me.

One of the things that you can do with macros is specify evaluation order, that's correct. You should also know when and how to evaluate your arguments, as done with the once-only utility (which is really just a thin wrapper around the obvious solution of binders).

But when I said "evaluation order has nothing to do with macros" I meant the kind of macro facility that you use (the original context of this thread was hygiene). No matter which kind of macros you have, the above issues are still the same.

-----

More