A schemy Lisp backed by Haskell
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
wiki @ b3f61e9
zepto-stdlib @ e324f2b


zepto banner general version GPL Licensed GHC Version Gitter

A simple Scheme interpreter in Haskell (originally based on this tutorial, extended massively, some code taken from husk-scheme). It implements a good enough subset of R5RS to make real programming possible, but does not strive for perfect compliance. I guess you could say it tries to be relaxed enough to not get into the way of other interesting things that may want to be implemented. Features included include but are not limited to the full numeric tower (with extensions even), macros, lazy evaluation, continuations, list comprehensions, user-defined types, an extensive standard library, many native primitives, a Haskell FFI, a native Regex datatype and help for all callables functions provided via docstrings included in their definition. And it actually has a decent shell with completion and history (written entirely in zepto itself).

It is pretty small, so the name might or might not be appropriate.

Table of Contents

  1. Maintainers
  2. Features
  3. Installation
  4. Introduction
  5. Libraries
  6. Contribute
  7. License



A few of the features were already listed above, but here is a full list of the cool and unusual:

  • A full numeric tower with the addition of a hardware integer datatype
  • list and hashmap comprehensions
  • regex literals
  • macros
  • laziness
  • continuations
  • a modern, awesome package manager (get it here)
  • prototype-based generic programming
  • a huge standard library, with libraries for testing, monads, parsing, CLI arguments, dates and time, and much more
  • an intuitive Haskell foreign function interface, supported by the package manager
  • #lang definitions that are somewhat similar to Racket

There is more, but those are my favorites by far.


After cloning via git (using the --recursive flag to make sure you get all the submodules), building is done via invoking cabal install after pulling the latest version of all linked submodules. Or make, because a plain old Makefile is included, too. After building you can run make test to check your installation.

A typical installation workflow through Cabal would look something like this:

git clone --recursive git://github.com/zepto-lang/zepto.git
cd zepto
cabal update
make test
# And then see whether none of the tests yield #f

Alternatively, you can use Stack (this makes it simpler if you don’t have GHC installed):

git clone --recursive git://github.com/zepto-lang/zepto.git
cd zepto
stack install
make test
# And then see whether none of the tests yield #f

There is also a Vim plugin for all of you terminal hackers (sorry emacs) and an Atom plugin.


DISCLAIMER: This is really only a quickstart. If you plan on going into it a bit deeper, you might want to take a look into the wiki. It all is a work in progress, though, so the resources might not be as exhaustive as would be appropriate.

If you know Scheme, working in the REPL should be pretty straightforward. Calling it via zepto, you should be greeted by this:

zepto Version 0.9.6, compiled with GHC version 710
Copyright (C) 2015 Veit Heller (GPL)
This is free software; see the accompanying LICENSE for copying conditions.
There is NO warranty whatsoever.
Hail Eris, all rites reversed.

Type ':quit' or press Ctrl-C to exit interpreter,
':help' to get a list of commands or ':license' to get the license text


Now you can just fiddle, maybe try something like

zepto> (pow 3 300) ; for schemers: this is a convenience alias for expt
=> 136891479058588375991326027382088315966463695625337436471480190078368997177499076593800

Please note that numerical types are promoted when they work together:

zepto> (+ 1 1.5)
=> 2.5

There are a few datatypes, namely integers, floats, rationals, exact and imaginary numbers, strings, lists, hashmaps and vectors. Quoted and quasi-quoted expressions are supported, too.

A faster version of integers, wrapping natives, is available. Please note that it is only available via explicitly creating it by invoking make-small on a regular integer. They will wrap around and overflow just as you would expect from a hardware integer. It string representation is with s-suffix. Whenever it comes in contact with other number types, expect it to be promoted. A quick demo:

zepto> (make-small 1)
=> 1s
zepto> (+ (make-small 100) 10) ; Fly away, you're an integer now!
=> 110
zepto> (make-small (pow 2 63)) ; Hardware-dependent
=> -9223372036854775808s

Remember: If you use small integers, you will have a bad time unless you know exactly what you are doing! They are unsafe, your mileage may vary.

If you need help with a specific primitive, invoke help on it like so:

zepto> (help +)
=> add two values
zepto> (help "+")
=> add two values

You can also get help for normal functions:

zepto> (define (x fst snd) "multiply two values" (* fst snd))
=> multiply two values; source: (lambda ("fst" "snd") ...)
zepto> (help x)
=> multiply two values; source: (lambda ("fst" "snd") ...)

And it autocompletes your newly created function, too!

There are also List and Hash Comprehensions. They look like this:

zepto> ; [do-this | for-every <- in, optional-check]
zepto> [(add1 x) | x <- [1 2 3 4]]
=> (2 3 4 5)
zepto> [(+ x 1) | x <- [1 2 3 4], (> x 1)]
=> (3 4 5)
zepto> #{(+ k 1) (* v 1.0) | k v <- #{1 2 3 4}}
=> #{2: 2.0, 4: 4.0, }
zepto> #{(+ k 1) v | k v <- #{1 2 3 4}, (= k 1)}
=> #{2: 2, }

The whitespaces matter. The thing is symbol-heavy enough as it is, so give it at least a bit of space.

Once you're done with the fiddling, just do:

zepto> :quit

Moriturus te saluto.

And you're back to your regular shell.

You can also run files or strings by handing them to the program like that:

$ cat t.scm
(display "hi")
$ zepto t.scm
$ zepto --single "(display \"bye\")"
$ cat test_args.scm
(display zepto:args)
$ zepto test_args.scm hi and bye
(hi and bye)

As you can see in the last two lines, command line arguments are available to Scheme via the zepto:args variable. It is a list of strings.

If you want to see examples of real programs, look in the examples directory.


There are many libraries already imported at startup, such as two random number generators(one of which is cryptographically secure) and most of the functions you know from your ordinary Scheme. There are also a few opt-ins.

There is currently no complete, comprehensive documentation for those modules, but you can find out about single commands via aforementioned help primitive.


Any contributions are welcome, be it in the form of code, feature requests or bug reports. Documentation is especially welcome, at all times.


Licensed under GPLv2. Copyright (c) 2014-2016, Veit Heller