Scheme-rs is a work-in-progress implementation of the R6RS specification of the scheme programming language that is designed to work with async Rust runtimes like tokio. In essence, it is a embedded scripting language for the async Rust ecosystem.
Scheme-rs is intended to be fully compliant with R6RS, and R7RS large when it is eventually released. To that end the bones are mostly there but some key issues remain.
Eventually, I would like scheme-rs to be more opinionated in the extras it provides, and include a package manager. That is obviously a long way away.
- Tail-call optimizations are fully supported
- Garbage Collected via Bacon-Rajan Concurrent Cycle Collection
- Most key forms (let/let*/letrec/lambda/define etc)
- Call by current continuation
- Transformers (define-syntax, syntax-case, make-variable-transformer)
- Spawning tasks and awaiting futures
- Exceptions and error handling
- Dynamic winding
- Ports and IO operations
- Most API functions are not implemented
- A large portion of lexical structures are missing; there's no way to specify recursive data structures
- And many more that I cannot think of off the top of my head
A REPL is the default entry point for scheme-rs at the current moment. You can access it by running cargo run
in the repo's root directory (examples taken from wikipedia):
~/scheme-rs> cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/scheme-rs`
>>> (let loop ((n 1))
... (if (> n 10)
... '()
... (cons n
... (loop (+ n 1)))))
$1 = (1 2 3 4 5 6 7 8 9 10)
>>> (let* ((yin
... ((lambda (cc) (display "@") cc) (call-with-current-continuation (lambda (c) c))))
... (yang
... ((lambda (cc) (display "*") cc) (call-with-current-continuation (lambda (c) c)))))
... (yin yang))
@*@**@***@****@*****@******@*******@********@*********@**********@***********@**********...^C
Scheme-rs provides a builtin
function attribute macro to allow you to easily define builtins. For example,
here is the definition of the number?
builtin in the source code:
#[builtin("number?")]
pub async fn is_number(
_cont: &Option<Arc<Continuation>>,
arg: &Gc<Value>,
) -> Result<Gc<Value>, RuntimeError> {
let arg = arg.read().await;
Ok(Gc::new(Value::Boolean(matches!(&*arg, Value::Number(_)))))
}