Cene is an easy-to-bootstrap Lisp dialect with purity and JS integration.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
buildlib Upgraded to the latest Lathe.js to deal with a deprecation warning. Oct 10, 2016
demos Replace the `stx` struct with a foreign value that can potentially ho… Dec 20, 2017
docs/source Add `regex-has-empty` to Cene. This is a check that `regex-while` and… Oct 29, 2018
lib-cene Fix typos in chops.cene. Dec 20, 2017
notes Add a design note to someday make `cline-by-own-method` consistent ab… Jan 3, 2018
src Add `regex-has-empty` to Cene. This is a check that `regex-while` and… Oct 29, 2018
test Version 0.0.35: Moved bigint/big-string, lens, and quasiquotation uti… Mar 10, 2017
.gitignore Added Sphinx docs. For now, they're just a really basic hello world. … Aug 6, 2016
.travis.yml Tried to set appropriate file modes. Mar 8, 2016
LICENSE.txt Updated the overall copyright date. Mar 8, 2017
README.md Fixed a broken link in the readme. Mar 10, 2017
build-demos.js Renamed several files to remove redundant references to Cene and Era. Mar 10, 2017
build-demos.sh Added `picky-javascript-quine`, which is like `sloppy-javascript-quin… Sep 18, 2016
build-docs.sh Added Sphinx docs. For now, they're just a really basic hello world. … Aug 6, 2016
cene.js Require `regex-{while,until}` loops to make progress. Rename `sinkErr… Oct 28, 2018
deploy.sh Fix a typo in deploy.sh. Apr 15, 2017
gh-pages.gitignore Tried to set appropriate file modes. Mar 8, 2016
index.html Updated copyright dates due to the move to the era-platform GitHub or… Mar 8, 2017
package.json Version 0.0.42: Fix typos in chops.cene. Dec 20, 2017



Travis build

Run some Cene code in your browser, logging to the console.

Run some reader unit tests in your browser, logging to the console.

Run the reader in your browser. This page is interactive!

See documentation of the built-in operations

Programming is an activity of last resort; as soon as we're programming, we're not directly in the field, in the moment, acting and responding as our needs arise. Yet we find ourselves in a new kind of field, a new kind of moment, where it starts to look valuable to solve the problem that is now at hand -- programming itself. Thus we eventually build things that are sophisticated enough to be programming languages of their own, but sadly they're incompatible with all the languages that came before. Cene fixes that. Cene is a programming language that's designed so that if you find yourself implementing a programming language, you can make a slight detour and implement Cene all over again instead.

Cene offers simple approaches to several problems that programming languages often fail to solve.

Cene's collapsed brackets are a simple syntactic feature that allows deep conditionals and continuation-passing code to be written as linear code blocks:

(if condition-1
  (if condition-2

(if condition-1
/if condition-2

  (fn result-1
    (do-thing-2 result-1
      (fn result-2
        (do-thing-3 result-1 result-2)))))

(do-thing-1 /fn result-1
/do-thing-2 result-1 /fn result-2
/do-thing-3 result-1 result-2)

Since Cene makes continuation-passing style so much more palatable, Cene's side effects are monadic, with asynchronous callbacks if they're observable to the outside world. If they're not observable to the outside world, they use a world-passing style instead. Cene code calls worlds "modes" and "modalities" to call to mind modal logic; world-passing code is effectively parameterized over possible worlds, and if a static type were ascribed to world-passing code, it might show similarities to a modal operator.

\= Copies one tree of UTF-8 files to another file tree recursively.
(defn copy-paths mode in out
  \= We read and branch on the type of the input file.
  (case (input-path-type mode in)
    \= If it's a directory, we loop over the directory listing.
    (foldr (input-path-directory-list mode in) (no-effects/nil)
    /fn item then
      \= We invoke a copy recursively, and in the meantime we proceed
      \= with other effects in the loop.
        (copy-paths mode
          (input-path-get in item)
          (output-path-get out item))
    \= If it's a blob, we read the blob as UTF-8 text and write it to
    \= the output path.
    (output-path-blob-utf-8 out /input-path-blob-utf-8 mode in)
  \= If we don't recognize the type of file, we just do nothing with
  \= it.

Cene's homoiconic syntax and macro support mean the built-in operators of the language don't look particularly better than the user-defined ones. This means you can focus on the operations you actually use rather than the ones the language anticipated that you'd use.

Cene supports sophisticated quasiquotation for strings. Cene code be moved into and out of Cene string literals without having to escape special characters, because the Cene string literal syntax is \;qq[...], and the same syntax doubles as an escape sequence dedicated to the purpose of generating Cene code strings. Together with string interpolations, quasiquotation variables, and collapsed brackets, we can write in a code-generating continuation-passing style if we ever really need to for some reason:

(do-thing-1 /fn result-1
    (do-thing-2 \;uq;ls`result-1` /fn result-2
      \;qq[(do-thing-3 \;uq;uq;ls`result-1` \;uq;ls`result-2`)])])

(do-thing-1 /fn result-1 \;(wq r1);qq/
\/do-thing-2 \;(rq r1);ls`result-1` /fn result-2 \;(wq r2);qq/
\/do-thing-3 \;(rq r1);ls`result-1` \;(rq r2);ls`result-2`)

Cene's quasiquotation for s-expressions will be just as full-featured.

Cene's first-class values all have the same capabilities: A value can be invoked as a function, and it can expose its contained values if the client can guess its name. Conceptually, even Cene's anonymous functions have names, but the names are just impossible to obtain (and therefore impossible to guess).

Cene has namespace support, which lets you make certain names impossible to obtain in certain files. Using this, you can enforce full encapsulation of your own data structures. Even the core data structures should be impossible to distinguish from ones that have been encapsulated this way, so Cene's design is consistent with a reflective tower of language implementations all running as part of the same program. This in turn means that if you find yourself implementing a new programming language for any reason, if it's anything like Cene, you will likely be able to continue development without sudden changes to your development flow. This support for smooth language development is the main driver behind Cene's design.

Even Cene's command-line tool is designed to invoke Cene code that implements a compiler (or more generally a build system). The Cene code is given a source directory, an output directory, and command line arguments. The Cene-to-JavaScript compiler is provided not as a command-line tool of its own, but as one of the built-in functions that helps this Cene code write content to its output directory.

Effects and error handling in Cene

Cene side effects were demonstrated above already, but here's the nitty-gritty:

Cene code is rather pure. Any Cene expression is referentially transparent and deterministic unless it stops prematurely due to resource exhaustion. Cene computations support general recursion and can therefore diverge, but divergence is treated as resource exhaustion.

When Cene needs to model side effects, it uses monads and world-passing techniques. These techniques are in full force when writing Cene macros, which have the power to read and write definitions and files as they go along. Cene code that interacts with JavaScript just uses a monad with the power to execute JavaScript code.

Currently, Cene code can terminate with an error at any time. This is considered a way to cause resource exhaustion on purpose; it's just a nicer alternative to an explicit infinite loop. Other basic operators in Cene use this mechanism for their dynamic errors.

Cene's approach to errors may be more flexible in the future. There's a plan for Cene code to be able to interact with its interpreter when it's having trouble, but we'll need to implement an interpreter that cares about these interactions before this will actually be useful. Once we have these, applications which make frequent use of custom interpreters and quoted/reified code will end up working a lot like dynamic scope, which will give the error handling system a flavor much like Common Lisp's condition/restart system.


As the overall Era project goes along, textual syntaxes like Cene's will eventually be regarded as a relic, replaced by monotonic code databases and hypertext syntaxes. Cene makes particular concessions for modern-day tools, namely text editors, command lines, and JavaScript runtimes.

Installation and usage

Install Node.js. Recent versions of Node.js come with npm.

Depending on your needs, you may want to install Cene locally or globally:

npm install cene
npm install --global cene

A global installation lets you easily invoke cene at the command line:

cene my-build.cene --in my-src-dir/ --out my-output-dir/ arg1 arg2

For a complete example project written in Cene, check out Cene Scaffold and follow the installation instructions in its readme.

Cene also supports being loaded as a Node.js library. It has only two exports:

var cene = require( "cene" );
cene.runCeneSync( cene.preludeFiles.concat( [ "my-build.cene" ] ), {
    args: [ "arg1", "arg2" ],
    in: "my-src-dir/",
    out: "my-output-dir/"
} );

If you use this technique to run Cene, it's a little bit more flexible than the command-line tool. You can supply more than one filename of your own to be executed in sequence, and you can choose not to include the prelude files, which the Cene command-line tool always includes.


Cene is named for -cene, a suffix that refers to eras, since Cene is part of the Era project. The resemblance to "Scheme" and the potential for wordplay with "seen," "scene," "sing," "sink," "obscene," and "zine" are just side benefits. Yes, I just spoiled all those puns for you. Go on and use the puns anyway, now fully equipped with the awareness of how stale they're going to get. A stale pun is still a good pun in the right context.

The brand image I imagine for Cene will have layers: In the meta layer, cartoon characters weild a mallet and a marker over a holographic void of pinks, yellows, and greens. In the marker-drawn layer, deep dark gray and pure white are punctuated by stamp symbols, gears, and dotted lines in luminescent red. When the gears hatch, cartoon vines grow out of them in a calm, neutral green. These cartoon vines are the brand image I have in mind for the Era project's eventual OS/IDE/visual programming language, so they should appear only for Cene projects that implement modular, reactive, or UI functionality as building blocks for Era.

About this project

Cene is released under the MIT license. See LICENSE.txt.