Rust-Ketos: ketos — A Lisp dialect functional programming language serving as a scripting and extension language for rust


Ketos is a Lisp dialect functional programming language.

The primary goal of Ketos is to serve as a scripting and extension language for programs written in the Rust programming language.

Ketos is compiled to bytecode and interpreted by pure Rust code.

API Documentation

ketos_derive Documentation

Language Documentation

Building the library

To build Ketos into your Rust project, add the following to your Cargo.toml:

ketos = "0.11"

And add the following to your crate root:

extern crate ketos;

Building the REPL

Build and run tests:

cargo test

Build optimized executable:

cargo build --release


ketos can be run as an interpreter to execute Ketos code files (.ket) or run as an interactive read-eval-print loop.


Ketos is distributed under the terms of both the MIT license and the Apache License (Version 2.0).



  • Bytecode binary format gets decoded on every function call?
    Bytecode binary format gets decoded on every function call?

    Aug 7, 2017

    I was noodling around in the code and discovered that, as far as I can tell, the bytecode interpreter decodes the binary instructions to Instruction when a function is called, not when the bytecode is loaded. This means that every time a function is called it has to re-decode all the instructions. On a benchmark program (fib implemented the dumb way) on Linux AMD64, it spent about 15% of the time in ketos::bytecode::Instruction::decode(), the highest of any function as recorded by perf.

    I haven't successfully made it decode the bytecode and store the result only once yet, but once I manage I'll post comparative benchmarks here.

  • Specify struct type in struct fields
    Specify struct type in struct fields

    Mar 29, 2018

    I tried to specify a struct name as a field type:

    ketos=> (struct foo ((a string)))
    ketos=> (struct bar ((b foo)))
    ketos=> (new bar :b (new foo :a "fails"))
      In main, lambda
      In system function new
    execution error: type error for field `b` of struct `bar`: expected foo; found struct: foo { a: "fails" }
    ketos=> (struct baz ((b struct)))
    ketos=> (new baz :b (new foo :a "works"))
    baz { b: foo { a: "works" } }

    The error confused me for a while until I realized the type for all struct values is "struct". Could it allow you to specify a struct type for a field?

    Also, the definition for bar was accepted, even though there is no such type foo - every attempt to create a bar will fail to typecheck!

  • How can a lambda call itself?
    How can a lambda call itself?

    Mar 24, 2019

    I'm trying to create a function that returns a function that calls itself. I tried this:

    (define (multo ex) (let ((f (lambda (n) (if
                            (= 0 n)
                            (* ex (f (- n 1))))))) f))
    (println "~s" ((multo 3) 3))

    But it doesn't work because the lambda can't refer to f inside the definition. How do I achieve this?

  • Add basic time module
    Add basic time module

    Mar 26, 2019

    This pull request would add a basic time module. The module provides two functions (utc-timestamp and local-timestamp) that return the current time in UTC or your local timezone as the number of seconds since January 1st 1970. The module uses chrono::offset to get the time in UTC and your local timezone and will add a dependency on chrono version 0.4.

    If anyone would like to write a more complete time library I can help. This module is currently an minimal implementation containing only what I need.

  • Project logo?
    Project logo?

    May 23, 2019

    I thought it might be fun to have a logo for the project ... I'll attach some ideas I had.

  • [feature request] function define in a function define
    [feature request] function define in a function define

    Jun 1, 2019

    ketos=> (define (x)
                (define (x-1) "hello")
      In main, define x
    execution error: type error: expected string; found list: (define (x-1) "hello")

    Would something like this be planned to be added to ketos? #60 is related in the sense that let-rec, and define in this way would achieve lambda recursion as well.

  • Cannot insert variable into global scope from within `let`.
    Cannot insert variable into global scope from within `let`.

    Aug 4, 2017

    Attempting to use define to insert a variable into the global scope from within a let statement throws an error. A minimal example to demonstrate

    extern crate ketos;
    use ketos::{Interpreter, FromValue};
    fn main() {
        let interp = Interpreter::new();
        match interp.run_code(r#"
            (let ((plus +) (mult *)) (
                (define ten (mult (plus 3 2) 2))
            "#, None) {
            Ok(k) => k,
            Err(e) => panic!(interp.display_error(&e)),
        let m = i32::from_value(interp.get_value("ten").unwrap()).unwrap();
        println!("{}", m);

    will throw

    execution error: type error: expected function; found name: ten

    In contrast, if we change the Ketos code executed to

    match interp.run_code(r#"
        (define ten (* (+ 3 2) 2))
    "#, None) {

    this code executes without error, printing 10.

  • Multithreading support?
    Multithreading support?

    Apr 1, 2017


    Does ketos support multithreading? Can I start a thread within ketos and return something from it? Futures? Mutexes?

    Would be nice to have these!

  • Trouble deriving attributes for structs with particular fields
    Trouble deriving attributes for structs with particular fields

    Jul 17, 2017

    I'm having a problem when trying to define a structure that creates a data structure such as a tree or list:

    #[derive(Clone, Debug, ForeignValue, FromValueClone, StructValue)]
    pub struct LList {
        data: String,
        point: Option<Box<LList>>,

    This causes the complier to throw errors related to point. I tried creating wrapper structures for Option and Box and deriving the attributes on those, but that failed.

    I hope I'm not missing something simple, I'm still a bit new to Rust.

  • Allow Global Scope clone
    Allow Global Scope clone

    Oct 19, 2016

    It is possible to allow cloning of the global scope?

    I have a server that receives many requests, I want to be able to execute code from a base scope from each request, without values or macros defined in any given request leaking into the global scope.

  • Add Bitwise operators
    Add Bitwise operators

    Sep 22, 2018

    I was very pleased to find this project, it was almost exactly what I was looking for. One thing I was missing was bitwise operators, so I implemented them. If this isn't what you want for ketos, go ahead and close the PR.

    My main concern is in the naming of the ketos functions: I see there is a tendency to use terse function names. so I made AND & and OR |. For XOR, I went with bit-xor since ^ is already being used for exponentiation. I considered naming the others bit-and and bit-or, but it didn't seem to be consistent with the shift functions.

    Since it's pre 1.0 software, I would assume there's some flexibility. I imagined you could:

    • rename ^ to pow (Maybe even stick it in the math module) and use &, | and ^ for bitwise.
    • all three could be named bit- while considering using something like shiftl and shiftr

    If you're interested, but would like some changes, please let me know! Thank you for building this project.

  • About sexp by sexp multi line editing
    About sexp by sexp multi line editing

    Sep 16, 2018

    I saw ketos' repl handles sexp across multi lines in the following way ::

    ketos=> (+ 1 2
    ketos(> 3
    ketos(> (+ 4 5
    ketos(> )
    ketos(> )

    Is it possible to use linefeed to support sexp by sexp multi line editing ? (like the repl of chez-scheme)

    (see also