A post on Lambda the Ultimate recently was about an Oberon interpreter written in Javascript, allowing Oberon to be embedded in the browser.
This reminded me of an near-R5RS compatible Scheme written in Javascript called jsScheme. It's a very nice implementation to play with but is IE 6 only. I've done some hacking at it and made it compatible with Firefox. I've put the modified version here.
The modified version has some changes to cater for Firefox and some things still don't work. JIT compilation for example, so I've left the checkbox for this disabled by default. This does slow things down quite a bit but I'll enable it again when I find the problem. For IE JIT works fine and you can enable it. If you find any other issues I'd be keen to get feedback. Leave a comment here or email me. I've also put the modified version in a darcs repository available with:
darcs get http://www.bluishcoder.co.nz/repos/javascript-scheme
Please send me any patches you'd like to apply.
What's nice about this system is it has most of R5RS scheme implemented, including tail recursion and first class continuations. A simple FFI for calling into Javascript is also available. For example, here's how to do some Javascript Date manipulation:
(define d (js-eval "new Date();"))
(display (js-invoke d "toString"))
(newline)
'js-eval' will evaluate Javascript code and return the object. 'js-invoke' will invoke methods on that object. There is also 'js-call' which can call Javascript functions. Unfortunately Firefox had problem using 'js-call' on things like 'alert' and 'setTimeout' so I wrapped these built-ins with Javascript functions which can be called:
(js-call (js-eval "jsAlert") "Hello World!")
'js-call' will a Javascript function object. The 'js-eval' was required to return that function object given the function name.
Lambda the Ultimate also pointed to a Links paper recently. In this paper they describe compiling the Links language to Javascript in continuation passing style and using this to implement user level threading for client side Javascript. This should be possible using this Scheme system as well. We can create continuations using call/cc and use the 'set-timeout!' function I added which wraps 'setTimeout' to do the scheduling. Here's an example of using 'set-timeout!'.
(set-timeout! (lambda () (alert "Hello World")) 1000)
This example waits one second and displays the alert. Any Scheme function can be passed, including continuations:
(define abort #f)
(define (yield)
(call/cc
(lambda (k)
(set-timeout! k 10)
(abort))))
(define (spawn thunk)
(call/cc
(lambda (k)
(set! abort k)
(thunk))))
(define (fib n)
(yield)
(if (= n 0)
0
(if (= n 1)
1
(+ (fib (- n 1)) (fib (- n 2))))))
This code allows you to spawn a single thread which will run in the background of the browser. That thread can call 'yield' to return control back to the browser for a brief time. A simple fibonacci function is provided which calls 'yield'. For example:
(spawn (lambda () (display (fib 10))))
This will return control immediately. You can carry on using the browser and the result will be displayed when the thread completes. A simple thread scheduler implementation would be an interesting exercise.