ArcLite: Arc for JavaScript
Date : 2008 02 08 Category : Tech & DevelopmentJonathan Tang has fallen into the Arc fun and took some time to fully implement it in JavaScript.
It differs from Paul's implementation:
Supports Unicode, at least as well as JavaScript supports it. There are very few I/O primitives - the ones that do exist write to the string "Eval.stdout", which can be tested and reset as necessary. I started work on a FFI but abandoned it so I could get back to my real projects. :-) The plan was to add 'jscall' and 'jsref' special forms that act as the function-call and object/array indexing operations, respectively. You can use Types.to_js() and Types.to_arc() to convert Arc values back and forth to JavaScript, along with the Types.* constructors themselves. String objects are immutable, because JavaScript does not offer mutable strings. (= (a-string 0) new-char) returns the new string, but does not modify the old one. A few functions in arclib have been redefined to account for this; see the #arclitelib element in this page's source. (set foo 1) operates in the lexical environment instead of always setting a global. It still sets a global if it can't find the symbol in the lexical environment. This is necessary for the Arc library to function. Tagged data items (eg. macros) can be called and set like their underlying data type. Continuations are not implemented. I had started on a CPS-based interpreter, but then realized that JavaScript lacks tail-call optimization and has a recursion limit of 1000 calls, which'll make any CPS-transformation blow the stack. Macros are looked up in the lexical environment, and are expanded right before the form is evaluated rather than when it is defined. This opens up a couple possibilities: You can theoretically have first-class macros; if you pass a macro value to a function, then the body of that function is expanded using the definition of the passed-in macro. You can have lexical macros. If you already have a binding defined in a function, then (mac ...) will set that binding rather than adding a new one to the global environment. This only applies if a binding already exists; if not, (mac ...) will create one in the global environment, just like the reference implementation. Other than this, the interpreter seems to execute arc.arc fine. Since arc.arc is considered the specification for Arc, I guess this means that ArcLite is a fully-conforming implementation. There may be a few bugs though; most functions are not well-tested, and I had to write my own reader implementation and primitive data types instead of piggybacking off Scheme. Don't expect a speed-demon. This is an AST-interpreter running on top of an interpreted language that's roughly 1000x slower than C. I'm surprised it works at all.Paul also put out an Arc Challenge:
Write a web program such that:
The first page of the program displays nothing but a text box and a submit button. You enter some arbitrary text and press the submit button, which takes you to … The second page is nothing but a single link labeled “click here”. The URL linked to must not contain the text entered in the first step (i.e. you are not supposed to pass the text as a parameter on the link). Clicking the link takes you to … The third page which contains “You said: XXX” (where XXX is the text you entered in the first step).Paul implemented this via:
[lisp]
(defop said req
(aform [w/link (pr "you said: " (arg _ "foo"))
(pr "click here")]
(input "foo")
(submit)))
[lisp]
Many others have thrown their hat in the ring too, including Jim Weirich:
PLAIN TEXT RUBY:Conversation.interact do |io|
text = io.ask
io.pause("click here")
io.tell("You said: #{text}")
end