Incrément (henceforth referred to as I7t) is a Scheme that supports and embraces an S-expression reader based on Clojure's (and also therefore EDN's) syntax. It is hosted atop Alex Shinn's Chibi Scheme.
Among other things, I7n provides a protocol facility and uses it
pervasively e.g. by supporting application of any object that supports
the core.appliable
/ apply
method or finding the length of any
collection which supports the core.col
/ length
method via the
len
procedure.
Aside from the semantics of application described above, I7t's
semantics are identical to Scheme. I7t is compiled into Scheme, and
the translated code preserves the position of tail expressions in the
resulting Scheme, so, unlike Clojure, I7t features TCO. (For this
reason I7t lacks the recur
form.)
Pull requests and issue submissions are welcomed.
Clone this repository and cd to its root directory. Run
chibi-scheme
. Evaluate (load "i7t.scm")
. The following procedures
of interest will now be available to you:
parse-i7t source [index]
: Parse and return a single I7t object from the source (a string or(chibi parse)
parse stream) atindex
or at the beginning if not specified.translate-i7t form
: Translate the I7t object into a Scheme S-expression suitable for evaluation.read-file-i7t filename
: Exhaustively parse file with namefilename
and return a list of I7t objects.expand-file-i7t filename
: Read I7t all expressions infilename
and return a list of translated S-expressions.load-i7t filename
: Expandfilename
and evaluate each translated S-expression.eval-i7t string
: Parse, translate, and evaluatestring
containing an I7t form.
At the Chibi REPL you can evaluate I7t like this:
> (eval-i7t "(loop iter [i 0] (if (> i 10) i (iter (inc i))))")
11
> ((eval-i7t "#([& xs] (apply + xs))") 1 2 3 4)
10
> (eval-i7t "(define-proc sum-of-squares [a b] (+ (* a a) (* b b)))")
> (sum-of-squares 3 4)
25
>
Or you can load and evalute files like this:
> (load-i7t "test/core.i7t")
Testing I7t language: ............
12 out of 12 (100.0%) tests passed in 0.04197406768798828 seconds.
>
Have fun!
One goal of I7t is to implement a reader that can be used as the basis
of data parsing or language development. The reader is available as
library (thunknyc i7t reader)
. If the reader fails to read a valid
Clojure source file please open an issue.
Another goal is to design and implement a language atop that reader. Subgoals of this effort:
- Embrace the increased expressive power of EDN to implement language forms that increase the concision, expressiveness, and readability of written code
- Strongly embrace R7RS and bake in type from the Red (and Tangerine and Orange and...) standardization processes
- Presume immutability
- Maintain compatibility with Scheme code i.e. Scheme->I7t and I7t->Scheme procedure applications should Just Work
And
,or
,if
,cond
Let
, behaving as Scheme'slet*
Loop
, behaving as Schemes named-let
Define-proc
for single and multiple arities with nested vector dereferencing,& rest-arguments
and:as all-arguments
supportDefine
Quote
(and traditional'foo
syntax)Test
from(chibi test)
- Procedure application allows strings, lists, vectors, sets, and hash tables to be applied
#([args ...] ...)
lambda form for single and multiple arities
- Quasi-quoting
- Serialization via `Edn-str' does not support most types at the moment
<nil>
: The symbolnil
represents the nil object andnil?
tests for it.<keyword>
: A variety of procedures construct, test, or convert keyword objects:keyword
,keyword?
,keyword-name
,keyword->string
,keyword->symbol
,string->keyword
,symbol->keyword
.
There are many things to like about Clojure, and none of them are inherently incompatible with inclusion in Scheme0. Among these features are its protocol orientation and its heavy bias toward immutability. The Scheme community has recognized the value of these features and has worked -- and continues to work -- to incorporate these features into the Scheme ecosystem.
The issue of Clojure's reader, however, attracts less enthusiasm within the community. I think this is mistaken. There are at least two kinds of improvements that Clojure's reader delivers.
The first kind is self-evident. The reader supports among other things keyword, set, and map literals. I have witnessed the benefits of the Clojure reader pooh-poohed based on the observation that none of these improvements, if incorporated into Scheme, would fundamentally increase the expressiveness of the language.
The second kind of improvement, however, is the one I believe is far more significant. One of the features of Lisp that writers note when discussing the language is that it is homoiconic, meaning that Lisp programs are written such that they are straightforwardly constructed from Lisp data structure literals and can be manipulated as such.
Clojure is a Lisp in this respect, but the syntax of the language does not merely allow hash tables and vectors to be read as literal data: the syntax embraces its core data structures and the creation of a much richer language.
Two areas where this embrace shines are in the forms for defining procedures with multiple arities and in its destructuring binding operations. Because of the power and concision and pervasive availability of these forms, especially destructuring in the context of procedure argument declarations and in let and other binding forms, most Clojure source files productively make use of these language features.
As another example, consider the lambda
form. In Clojure, the lambda
form (named fn
) allows the function to be named by writing e.g. (fn loop [arg1 arg2] ...)
. It would be a useful to be able to do this in
Scheme as well, but a cascade of design decisions and limitations
makes this feature impossible. A transliteration of this form into
Scheme yields (lambda loop #(arg1 arg2) ...)
, which is meaningful: a
list of the procedure's arguments are bound to loop
and then #(arg1 arg2)
is evaluated. Clojure's way of doing this is to write (fn [& args] ...)
, which allows the original Clojure form above to be
recognized as a name procedure definition. Doing this in Scheme would
require adding parenthses or supporting (lambda (. rest) ...)
, which
is nonsensical in Scheme. The sad part of this is that thanks to
Scheme's support for tail call optimization, this facility would be
far more useful in Scheme than it is in Clojure, which sets up the
lambda as the target of enclosed recur
calls.
MIT License.