A Scheme/Racket inspired library for Common Lisp.
- Lisp-2 (separate function and variable namespaces) definition macro, which binds values/functions in both namespaces
- Clean
[...]
syntax for(FUNCALL ...)
- Clean
- Lisp-1 (unified function and variable namespace) definition macro, which performs code-walking to achieve a single namespace
- Compact, scheme-style argument lists with an easy syntax for specifying optional, keyword, rest, and ignorable arguments
- Enriched package functions which bring packages to the level of first class objects
- Optional guard clauses which are enforced by functions, and provide documentation
- Enriched documentation for functions allow objects other than strings
- A markup language which can be rendered as plain-text or HTML, which can be used for rich documentation
- Named
LET
for generating a locally recursive form - Scheme-inspired
STREAM
s andBUNDLE
s - Record-style
DEFINE-STRUCT
- Utility functions for: lists, alists, list-based sets, hash-tables, procedures, procedure arities, strings
CUT
macro for easily creating curried functions- A code transformer for transforming SEXP-based languages
- TODO: Planned implementation of delimited continuations with similar semantics to
CONTROL
/RUN
as used by Racket- These continuations would be respectful of dynamic forms such as
CATCH
andUNWIND-PROTECT
- These continuations would be respectful of dynamic forms such as
The SCHEMEISH package can be used in place of CL.
(defpackage my-new-package
(:use #:schemeish))
With a few minor exceptions Schemeish is designed to enhance Common Lisp by providing new functionality, rather than replacing exisiting symbols. The following CL symbols are replaced by Schemeish:
LAMBDA
uses scheme-style argument lists with nestable defines.LET
is fully compatible withCL:LET
, but with additionaly named let syntaxSORT
is a non-modifying sort for listsMAP
maps a function over a list (equivalent toMAPCAR
)STREAM
constructs a Scheme-style stream of elements.
The rest of Schemeish is fully compatible with the CL
package. If you'd like to favor CL
in conflicts you can use :SHADOWING-IMPORT-FROM
.
(defpackage my-streamless-package
(:use #:schemeish)
(:shadowing-import-from #:cl #:stream))
Schemeish provides:
[...]
syntax which expands to(funcall ...)
.- #G(guard-clauses...) syntax which is used to annotate function-definitions. These create guard clauses for functions as well as for documentation.
- #Ddocumentation-source syntax which is used to annotate function-definitions. These enable rich documentation (more than just documentation-strings).
A documentation-source is any object which implements the
DOCUMENTATION-STRING
method.
To use Schemeish syntax you can use INSTALL-SYNTAX!
and to disable it again you can use UNINSTALL-SYNTAX!
.
Typically this is done at the start/end of each file, but it may be helpful to leave out the UNINSTALL-SYNTAX!
during development.
(install-syntax!)
[(lcurry #'+ 2) 1 2 3] ;; => 8
#g((number? x) (string? y)) ;; => #g((number? x) (string? y))
#d(format nil "Hello") ;; => #d(format nil "Hello")
At the top-level, DEFINE
defines a global function.
DEFINE
takes one of three forms
(define name [documentation] function)
defines a function named name
(define 2+ "Adds (+ 2 args...)" (lcurry #'+ 2)) ;; => 2+
(2+ 1 2 3) ;; => 8
(documentation #'2+ t) ;; => "Adds (+ 2 args...)"
(define (name . scm-parameters) function-body...)
defines a function named name with the givenscm-parameters
andfunction-body
(define (frobnicate a b (:keyword1 3))
(list a b keyword1)) ;; => frobnicate
(frobnicate 1 2 :keyword1 'c) ;; => (1 2 C)
(define ((...) . scm-parameters) function-body...)
defines a function which takesscm-parameters
and returns a closure whose body is function-body
(define (((nested-foo a) b) . c)
(list* a b c)) ;; => nested-foo
[[(nested-foo 1) 2] 3 4 5] => (1 2 3 4 5)
(funcall (funcall (nested-foo 1) 2) 3 4 5) => (1 2 3 4 5)
Function bodies in Schemeish take documentation objects, guard tags, and expand defintions in a lexical-body.
A function body takes the form ([documentation] [guard] lexical-body...)
If provided, documentation
may either be a documentation-tag
(a documentation object prepended with "#D") or a string.
- A
documentation-tag
's object is evaluated each time the function is compiled. If provided,guard
is aguard-tag
(an unevaluated list of guard clauses prepended with "#G")
(define (guarded-foo index vector)
#d(format nil "Returns the value at (aref vector index)")
#g((number? index) (vectorp vector) (< index (length vector)))
(aref vector index)) ;; => guarded-foo
(guarded-foo 3 #(1 2)) ;; => ERROR: Failed function guard-clause: (< INDEX (LENGTH VECTOR))
(guarded-foo 1 #(1 2)) ;; => 2
(documentation #'guarded-foo t)
;; =>
"Returns the value at (aref vector index)
Parameters: (INDEX VECTOR)
Definition Form: (GUARDED-FOO INDEX VECTOR)
GUARDED-FOO has the following guard clauses:
((NUMBER? INDEX) (VECTORP VECTOR) (< INDEX (LENGTH VECTOR)))"
A LEXICAL-BODY
in Schemeish expands mutually recursive definitions (a la LETREC
or LABELS
).
There are two types of lexical-body
s: Lisp-1 style (unified function/variable namespace) and Lisp-2 style (separate function/variable namespaces).
A lexical-body
takes the form (definitions declarations forms...)
, where definitions are lexical-body-definition
s, declarations are of the form (cl:declare ...)
,
and forms... are evaluated in an implicit progn.
The following definition forms are available by default:
DEFINE
andDEF
: Follows the same rules asDEFINE
, but defines a local function usinglabels
, and a variable usinglet
.(DEFINE-VALUES (names...) values-form)
: Usesmultiple-value-setq
to assign names(DEFINE-DESTRUCTURING destructuring-lambda-list form)
: Usesdestructuring-bind
to assign values to the parameters named bydestructuring-lambda-list
(define (foo)
(define-values (number remainder) (truncate 3 4))
(define-destructuring (&whole whole a b c &rest more)
(list :a :b :c :r1 :r2 :r3))
(define v :v)
(define (f x) (list 'f x))
(define (((g x) y) z) `(((g ,x) ,y) ,z))
(list number remainder
whole a b c more
v
(f :x)
[[(g :x) :y] :z])) ;; => foo
(foo) ;; => (0 3 (:A :B :C :R1 :R2 :R3) :A :B :C (:R1 :R2 :R3) :V (F :X) (((G :X) :Y) :Z))
More definition forms can be added using register-lexical-body-definition
and register-lexical-body2-definition
(for Lisp-2 style).
lexically
can be used to create an explicit lisp-2 style lexical-body
scm
(see DEF
) can be used to create an explicit lisp-1 style lexical-body
At the top-level DEF
is very similar to DEFINE
, except that it performs code-transformation to create a Lisp-1 style unified function/variable namespace.
The SCM
macro performs the code transformation, and can be used to create Lisp-1 contexts.
The following are the same examples as in DEFINE
, but using DEF
instead.
(def 2+ "Adds (+ 2 args...)" (lcurry + 2)) ;; => 2+
(2+ 1 2 3) ;; => 8
(scm (documentation 2+ t)) ;; => "Adds (+ 2 args...)"
(def (frobnicate a b (:keyword1 3))
(list a b keyword1)) ;; => frobnicate
(frobnicate 1 2 :keyword1 'c) ;; => (1 2 C)
(def (((nested-foo a) b) . c)
(list* a b c)) ;; => nested-foo
(scm (((nested-foo 1) 2) 3 4 5)) => (1 2 3 4 5)
Schemeish provides enriched package utilities. Some examples:
(package-symbols package)
: returns a list of all symbols in package(defpackage-form package)
: Returns aDEFPACKAGE
form(hierarchical-defpackage-forms packages)
: Returns a list ofDEFPACKAGE
forms that have been organized hierarchically for use inpackage.lisp
((package-re-export-shadowing . re-exported-packages) package)
: Uses and re-exportsre-exported-packages
frompackage
.- Many more
Guard clauses provide both documentation and enforcement:
(define (guarded-foo index vector)
#d(format nil "Returns the value at (aref vector index)")
#g((number? index) (vectorp vector) (< index (length vector)))
(aref vector index)) ;; => guarded-foo
(guarded-foo 3 #(1 2)) ;; => ERROR: Failed function guard-clause: (< INDEX (LENGTH VECTOR))
(guarded-foo 1 #(1 2)) ;; => 2
(documentation #'guarded-foo t)
;; =>
"Returns the value at (aref vector index)
Parameters: (INDEX VECTOR)
Definition Form: (GUARDED-FOO INDEX VECTOR)
GUARDED-FOO has the following guard clauses:
((NUMBER? INDEX) (VECTORP VECTOR) (< INDEX (LENGTH VECTOR)))"
The runtime-enforcement of guard-clauses can be dynamically circumvented using with-guard-clauses-disabled
[(lambda (x y) #g((number? x) (string? y)) (list x y)) nil nil] ;; => ERROR: Failed function guard-clause: (NUMBER? X)
(with-guard-clauses-disabled [(lambda (x y) #g((number? x) (string? y)) (list x y)) nil nil]) ;; => (nil nil)
Schemeish provides an extensible markup language and a means of providing richer text for documentation. This can be used to generate both human-readable docstrings as well as HTML. Schemeish provides a basic text renderer for its markup language, which renders to Markdown-like plain text.
(define (fancy-docs)
#d(paragraph "This is some fancy documentation with "
(italic (inline-text "italics"))
" with "
(bold (inline-text "bolds"))
" with " (br)
(block-quote (inline-text "Quotes")))
:ok) ;; => fancy-docs
(documentation #'fancy-docs t)
;; =>
"This is some fancy
documentation with
_italics_ with
*bolds* with
> Quotes
Parameters: NIL
Definition Form: (FANCY-DOCS)
FANCY-DOCS has no guard clauses."
(type-of (object-documentation-source #'fancy-docs)) ;; markup
Any object which implements the documentation-string
method can be used with documentation-tag
s.
In all cases the LET
provided by Schemeish works the same as CL:LET
Additionally you can create a local named procedure with LET
(let recurse ((result 1)
(n 5))
(if (= n 0)
result
(recurse (* result n) (1- n))))
Cut provides a macro for creating curried functions using placeholders.
(scm ((cut (list 1 _ 3 . _)) 2 4 5 6)) ;; => (1 2 3 4 5 6)
AND-LET* will evaluate each clause from first to last until one is false. If all are true, evaluate body. Each clause is one of: identifier, (expression), or (identifier expression). If the clause is (identifier expression) it creates a binding for the rest of the clauses and the body.
(and-let* ((list (compute-list))
((pair? list))
(item (car list))
((integer? item)))
(sqrt item))
Provides functions for creating and manipulating streams e.g.: stream-cons
, stream-append
, stream-map
.
Provides the stream-collect
macro for efficient implementation of combinatorial algorithms.
(define (prime? num)
(let ((root (floor (sqrt num))))
(not (find-if (lambda (div) (zerop (rem num div))) (range (1+ root) 2)))))
(define (prime-sum-pairs n)
(stream-collect
(list i j (+ i j))
((i (stream-range 1 n))
(j (stream-range 1 (1- i))))
(prime? (+ i j))))
(stream->list (prime-sum-pairs 6)) ;; => ((2 1 3) (3 2 5) (4 1 5) (4 3 7) (5 2 7) (6 1 7) (6 5 11))))
Define-struct provides a syntax for creating records. See documentation of DEFINE-STRUCT
.
The underlying type is a CLOS object.
Transparent structs (the default) can be deep-compared using equal?
, and have a readable implementation of the print-object
method.
(define-struct p2
(x y)) ;; => (P2 MAKE-P2 P2? P2-X P2-Y)
(p2? (make-p2 3 4)) ;; => T
(let ((p (make-p2 3 4)))
(sqrt (+ (* (p2-x p) (p2-x p))
(* (p2-y p) (p2-y p))))) ;; => 5.0
Structs have simple inheritance (one parent).
(define-struct p3
(z)
:super 'p2) ;; => (P3 MAKE-P3 P3? P3-Z)
(p2? (make-p3 1 2 3)) ;; => T
(p3? (make-p3 1 2 3)) ;; => T
(make-p3 1 2 3) ;; => (make-p3 1 2 3)
(def x p2-x)
(def y p2-y)
(def z p2-z)
(let ((p (make-p3 1 2 3)))
(list (x p) (y p) (z p))) ;; => (1 2 3)
By default structs are immutable, but SET-
functions can be generated.
There are quite a number of general purpose utilities. Here are a few interesting ones:
- Alist functions for creating and manipulating association-lists
(group key-fn list)
Groups elements of list that have the same key-fn into an alist.(hash-map table proc)
Maps [proc key value] over the keys and values of table, producing a list as a result.(has-specific-arity? arity-list fixed-arity-n)
Returns true if an arity-list (retrieved from procedure-arity) has the specific fixed arity.(string-map proc string)
Applies proc to each character in string, returning a new string of the results appended together. Proc is expected to return a character or string
A code transformer (or code walker) can transform a SEXP-based expression from language to another, expanding any macros along the way.
The transformation is defined by a TRANSFORMER
structure, which has transform functions for special forms, lists (function application in CL), and atoms.
Each transform function has access to the the transformer, the expression being transformed, and the environment (from a macro's &ENVIRONMNENT
).
See scm.lisp
for an example usage of code-transformation.
Bundles are closure objects which provide an object-oriented style interface. See documentation of BUNDLE
.
The following adjustments to emacs init.el enhance the experience:
(defun enable-squares-as-parens-in-syntax-table (syntax-table)
(modify-syntax-entry ?\[ "(]" syntax-table)
(modify-syntax-entry ?\] ")[" syntax-table))
(enable-squares-as-parens-in-syntax-table lisp-mode-syntax-table)
(define-key paredit-mode-map (kbd "M-{")
'paredit-wrap-square)
(defun swap-square-and-round ()
"Change |(..) to |[..]. | is point position."
(interactive)
(let* ((char (string (char-after)))
(paren? (string= char "("))
(square? (string= char "[")))
(cond
(paren? (paredit-open-square 1))
(square? (paredit-open-round 1)))
(when (or paren? square?)
(right-char 1)
(paredit-splice-sexp)
(left-char 1))))
(define-key paredit-mode-map (kbd "M-[") 'swap-square-and-round)
MIT